fix: upgrade all component to fix biomejs errors

This commit is contained in:
p-sw 2024-06-30 14:17:59 +09:00
parent 417deb4aa6
commit 8c7f9bd257
17 changed files with 196 additions and 98 deletions

View File

@ -1,8 +1,9 @@
import { type AsChild, Slot, type VariantProps, vcn } from "@pswui-lib";
import React from "react";
import { vcn, VariantProps, Slot, AsChild } from "@pswui-lib";
const colors = {
disabled: "disabled:brightness-50 disabled:cursor-not-allowed disabled:opacity-50 disabled:saturate-50",
disabled:
"disabled:brightness-50 disabled:cursor-not-allowed disabled:opacity-50 disabled:saturate-50",
outline: {
focus: "dark:focus-visible:outline-white/20 focus-visible:outline-black/10",
},
@ -118,7 +119,12 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
className: buttonVariants(variantProps),
};
return <Comp ref={ref} {...compProps} />;
return (
<Comp
ref={ref}
{...compProps}
/>
);
},
);

View File

@ -1,5 +1,5 @@
import { type VariantProps, vcn } from "@pswui-lib";
import React from "react";
import { VariantProps, vcn } from "@pswui-lib";
const checkboxColors = {
background: {
@ -99,10 +99,11 @@ const Checkbox = React.forwardRef<HTMLInputElement, CheckboxProps>(
viewBox="0 0 24 24"
className={`${checked ? "opacity-100" : "opacity-0"} transition-opacity duration-75 ease-in-out`}
>
<title>checked</title>
<path
fill="currentColor"
d="M21 7L9 19l-5.5-5.5l1.41-1.41L9 16.17L19.59 5.59z"
></path>
/>
</svg>
</label>
</>

View File

@ -1,12 +1,12 @@
import { Slot, type VariantProps, vcn } from "@pswui-lib";
import React, { useState } from "react";
import { Slot, VariantProps, vcn } from "@pswui-lib";
import ReactDOM from "react-dom";
import {
DialogContext,
type IDialogContext,
initialDialogContext,
useDialogContext,
IDialogContext,
} from "./Context";
/**
@ -55,7 +55,7 @@ const DialogTrigger = ({ children }: DialogTriggerProps) => {
*/
const [dialogOverlayVariant, resolveDialogOverlayVariant] = vcn({
base: "fixed inset-0 z-50 w-full h-full max-w-screen transition-all duration-300 flex flex-col justify-center items-center",
base: "fixed inset-0 z-50 w-full h-screen overflow-y-auto max-w-screen transition-all duration-300",
variants: {
opened: {
true: "pointer-events-auto opacity-100",
@ -72,9 +72,9 @@ const [dialogOverlayVariant, resolveDialogOverlayVariant] = vcn({
lg: "backdrop-brightness-50",
},
padding: {
sm: "p-4",
md: "p-6",
lg: "p-8",
sm: "[&>div]:p-4",
md: "[&>div]:p-6",
lg: "[&>div]:p-8",
},
},
defaults: {
@ -114,7 +114,14 @@ const DialogOverlay = React.forwardRef<HTMLDivElement, DialogOverlay>(
onClick?.(e);
}}
>
{children}
<div
className={
"w-screen max-w-full min-h-screen flex flex-col justify-center items-center"
}
>
{/* Layer for overflow positioning */}
{children}
</div>
</div>,
document.body,
)}
@ -170,23 +177,27 @@ const [dialogContentVariant, resolveDialogContentVariant] = vcn({
},
});
interface DialogContent
interface DialogContentProps
extends React.ComponentPropsWithoutRef<"div">,
Omit<VariantProps<typeof dialogContentVariant>, "opened"> {}
const DialogContent = React.forwardRef<HTMLDivElement, DialogContent>(
const DialogContent = React.forwardRef<HTMLDivElement, DialogContentProps>(
(props, ref) => {
const [{ opened }] = useDialogContext();
const [variantProps, otherPropsCompressed] = resolveDialogContentVariant({
...props,
opened,
});
const { children, ...otherPropsExtracted } = otherPropsCompressed;
const { children, onClick, ...otherPropsExtracted } = otherPropsCompressed;
return (
<div
{...otherPropsExtracted}
ref={ref}
className={dialogContentVariant(variantProps)}
onClick={(e) => {
e.stopPropagation();
onClick?.(e);
}}
>
{children}
</div>

View File

@ -1,4 +1,9 @@
import { Dispatch, SetStateAction, useContext, createContext } from "react";
import {
type Dispatch,
type SetStateAction,
createContext,
useContext,
} from "react";
/**
* =========================

View File

@ -1,13 +1,13 @@
import { type AsChild, Slot, type VariantProps, vcn } from "@pswui-lib";
import React, {
ComponentPropsWithoutRef,
TouchEvent as ReactTouchEvent,
type ComponentPropsWithoutRef,
type TouchEvent as ReactTouchEvent,
forwardRef,
useContext,
useEffect,
useRef,
useState,
} from "react";
import { AsChild, Slot, VariantProps, vcn } from "@pswui-lib";
import { createPortal } from "react-dom";
interface IDrawerContext {
@ -57,8 +57,7 @@ const DrawerRoot = ({ children, closeThreshold, opened }: DrawerRootProps) => {
opened: opened ?? prev.opened,
closeThreshold: closeThreshold ?? prev.closeThreshold,
}));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [closeThreshold, opened]);
}, [closeThreshold, opened, setState]);
return (
<DrawerContext.Provider value={state}>{children}</DrawerContext.Provider>
@ -302,8 +301,7 @@ const DrawerContent = forwardRef<HTMLDivElement, DrawerContentProps>(
window.removeEventListener("touchmove", onMouseMove);
window.removeEventListener("touchend", onMouseUp);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [state, dragState, position]);
}, [state, setState, dragState, position]);
return (
<div

View File

@ -1,5 +1,5 @@
import { type VariantProps, vcn } from "@pswui-lib";
import React from "react";
import { VariantProps, vcn } from "@pswui-lib";
const inputColors = {
background: {
@ -92,7 +92,7 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>((props, ref) => {
const innerRef = React.useRef<HTMLInputElement | null>(null);
React.useEffect(() => {
if (innerRef && innerRef.current) {
if (innerRef?.current) {
innerRef.current.setCustomValidity(invalid ?? "");
}
}, [invalid]);

View File

@ -1,5 +1,5 @@
import { type VariantProps, vcn } from "@pswui-lib";
import React from "react";
import { VariantProps, vcn } from "@pswui-lib";
const [labelVariant, resolveLabelVariantProps] = vcn({
base: "has-[input[disabled]]:brightness-75 has-[input[disabled]]:cursor-not-allowed has-[input:invalid]:text-red-500",

View File

@ -1,5 +1,5 @@
import { type VariantProps, vcn } from "@pswui-lib";
import React from "react";
import { VariantProps, vcn } from "@pswui-lib";
const switchColors = {
background: {

View File

@ -1,7 +1,7 @@
import { AsChild, Slot, VariantProps, vcn } from "@pswui-lib";
import { type AsChild, Slot, type VariantProps, vcn } from "@pswui-lib";
import React from "react";
import { TabContextBody, TabContext, Tab } from "./Context";
import { type Tab, TabContext, type TabContextBody } from "./Context";
interface TabProviderProps {
defaultName: string;
@ -30,7 +30,12 @@ interface TabListProps
const TabList = (props: TabListProps) => {
const [variantProps, restProps] = resolveTabListVariantProps(props);
return <div className={TabListVariant(variantProps)} {...restProps} />;
return (
<div
className={TabListVariant(variantProps)}
{...restProps}
/>
);
};
const [TabTriggerVariant, resolveTabTriggerVariantProps] = vcn({
@ -76,7 +81,7 @@ const TabTrigger = (props: TabTriggerProps) => {
});
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [name]);
}, [name, setContext]);
const Comp = props.asChild ? Slot : "button";
@ -119,18 +124,18 @@ const TabContent = (props: TabContentProps) => {
const { name, ...restProps } = restPropsBeforeParse;
const [context] = React.useContext(TabContext);
if (context.active[1] === name) {
return (
<Slot
className={tabContentVariant({
...variantProps,
})}
{...restProps}
/>
);
} else {
if (context.active[1] !== name) {
return null;
}
return (
<Slot
className={tabContentVariant({
...variantProps,
})}
{...restProps}
/>
);
};
export { TabProvider, TabList, TabTrigger, TabContent };

View File

@ -1,20 +1,20 @@
import { type VariantProps, vcn } from "@pswui-lib";
import React, { useEffect, useId, useRef } from "react";
import ReactDOM from "react-dom";
import { VariantProps, vcn } from "@pswui-lib";
import { toastVariant } from "./Variant";
import {
ToastOption,
toasts,
subscribeSingle,
getSingleSnapshot,
notifySingle,
type ToastOption,
close,
notify,
defaultToastOption,
subscribe,
getSingleSnapshot,
getSnapshot,
notify,
notifySingle,
subscribe,
subscribeSingle,
toasts,
} from "./Store";
import { toastVariant } from "./Variant";
const ToastTemplate = ({
id,
@ -74,7 +74,7 @@ const ToastTemplate = ({
);
transitionDuration = style
? {
value: parseFloat(style[1] ?? "0"),
value: Number.parseFloat(style[1] ?? "0"),
unit: style[3] ?? style[2] ?? "s",
}
: null;
@ -96,7 +96,7 @@ const ToastTemplate = ({
}, calculatedTransitionDuration);
return () => clearTimeout(timeout);
}
}, [id, toastData.life, toastData.closeTimeout, toastData.closeButton]);
}, [id, toastData.life, toastData.closeTimeout]);
return (
<div
@ -107,13 +107,18 @@ const ToastTemplate = ({
ref={ref}
>
{toastData.closeButton && (
<button className="absolute top-2 right-2" onClick={() => close(id)}>
<button
className="absolute top-2 right-2"
onClick={() => close(id)}
type={"button"}
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="1.2rem"
height="1.2rem"
viewBox="0 0 24 24"
>
<title>Close</title>
<path
fill="currentColor"
d="M19 6.41L17.59 5L12 10.59L6.41 5L5 6.41L10.59 12L5 17.59L6.41 19L12 13.41L17.59 19L19 17.59L13.41 12z"
@ -167,7 +172,7 @@ const Toaster = React.forwardRef<HTMLDivElement, ToasterProps>((props, ref) => {
if (toasterInstance && id !== toasterInstance.id) {
if (process.env.NODE_ENV === "development" && !muteDuplicationWarning) {
console.warn(
`Multiple Toaster instances detected. Only one Toaster is allowed.`,
"Multiple Toaster instances detected. Only one Toaster is allowed.",
);
}
return null;

View File

@ -1,4 +1,4 @@
import { addToast, update, close } from "./Store";
import { addToast, close, update } from "./Store";
export function useToast() {
return {

View File

@ -1,4 +1,4 @@
import { ToastBody } from "./Variant";
import type { ToastBody } from "./Variant";
export interface ToastOption {
closeButton: boolean;
@ -54,11 +54,11 @@ export function getSingleSnapshot(id: `${number}`) {
}
export function notify() {
subscribers.forEach((subscriber) => subscriber());
for (const subscriber of subscribers) subscriber();
}
export function notifySingle(id: `${number}`) {
toasts[id].subscribers.forEach((subscriber) => subscriber());
for (const subscriber of toasts[id].subscribers) subscriber();
}
export function close(id: `${number}`) {

View File

@ -1,4 +1,4 @@
import { VariantProps, vcn } from "@pswui-lib";
import { type VariantProps, vcn } from "@pswui-lib";
const toastColors = {
background: "bg-white dark:bg-black",

View File

@ -1,2 +1,2 @@
export { Toaster } from "./Component";
export { useToast } from "./Hook";
export { useToast } from "./Hook";

View File

@ -1,5 +1,5 @@
import { type AsChild, Slot, type VariantProps, vcn } from "@pswui-lib";
import React, { useState } from "react";
import { AsChild, Slot, VariantProps, vcn } from "@pswui-lib";
interface TooltipContextBody {
position: "top" | "bottom" | "left" | "right";

View File

@ -1,5 +1,5 @@
import { twMerge } from "tailwind-merge";
import React from "react";
import { twMerge } from "tailwind-merge";
/**
* Merges the react props.
@ -60,14 +60,15 @@ function mergeReactProps(
* @returns The single ref.
*/
function combinedRef<I>(refs: React.Ref<I | null>[]) {
return (instance: I | null) =>
refs.forEach((ref) => {
return (instance: I | null) => {
for (const ref of refs) {
if (ref instanceof Function) {
ref(instance);
} else if (ref) {
(ref as React.MutableRefObject<I | null>).current = instance;
}
});
}
};
}
interface SlotProps {

View File

@ -57,6 +57,31 @@ type VariantKV<V extends VariantType> = {
[VariantKey in keyof V]: BooleanString<keyof V[VariantKey] & string>;
};
/**
* Used for safely casting `Object.entries(<VariantKV>)`
*/
type VariantKVEntry<V extends VariantType> = [
keyof V,
BooleanString<keyof V[keyof V] & string>,
][];
/**
* Takes VariantKV as parameter, return className string.
*
* @example
* vcn({
* /* ... *\/
* dynamics: [
* ({ a, b }) => {
* return a === "something" ? "asdf" : b
* },
* ]
* })
*/
type DynamicClassName<V extends VariantType> = (
variantProps: VariantKV<V>,
) => string;
/**
* Takes VariantType, and returns a type that represents the preset object.
*
@ -94,6 +119,7 @@ export function vcn<V extends VariantType>(param: {
*/
base?: string | undefined;
variants: V;
dynamics?: DynamicClassName<V>[];
defaults: VariantKV<V>;
presets?: undefined;
}): [
@ -108,7 +134,7 @@ export function vcn<V extends VariantType>(param: {
/**
* Any Props -> Variant Props, Other Props
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
// biome-ignore lint/suspicious/noExplicitAny: using unknown causes error `Index signature for type 'string' is missing in type --Props`.
<AnyPropBeforeResolve extends Record<string, any>>(
anyProps: AnyPropBeforeResolve,
) => [
@ -124,6 +150,7 @@ export function vcn<V extends VariantType, P extends PresetType<V>>(param: {
*/
base?: string | undefined;
variants: V /* VariantType */;
dynamics?: DynamicClassName<V>[];
defaults: VariantKV<V>;
presets: P;
}): [
@ -139,7 +166,7 @@ export function vcn<V extends VariantType, P extends PresetType<V>>(param: {
/**
* Any Props -> Variant Props, Other Props
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
// biome-ignore lint/suspicious/noExplicitAny: using unknown causes error `Index signature for type 'string' is missing in type --Props`.
<AnyPropBeforeResolve extends Record<string, any>>(
anyProps: AnyPropBeforeResolve,
) => [
@ -159,19 +186,58 @@ export function vcn<
>({
base,
variants,
dynamics = [],
defaults,
presets,
}: {
base?: string | undefined;
variants: V;
dynamics?: DynamicClassName<V>[];
defaults: VariantKV<V>;
presets?: P;
}) {
/**
* --Internal utility function--
* After transforming props to final version (which means "after overriding default, preset, and variant props sent via component props")
* It turns final version of variant props to className
*/
function __transformer__(
final: VariantKV<V>,
dynamics: string[],
propClassName?: string,
): string {
const classNames: string[] = [];
for (const [variantName, variantKey] of Object.entries(
final,
) as VariantKVEntry<V>) {
classNames.push(variants[variantName][variantKey.toString()]);
}
return twMerge(base, ...classNames, ...dynamics, propClassName);
}
return [
/**
* Takes any props (including className), and returns the class name.
* If there is no variant specified in props, then it will fallback to preset, and then default.
*
*
* Process priority of variant will be:
*
* --- Processed as string
* 1. Base
*
* --- Processed as object (it will ignore rest of "not duplicated classname" in lower priority)
* 2. Default
* 3. Preset (overriding default)
* 4. Variant props via component (overriding preset)
*
* --- Processed as string
* 5. Dynamic classNames using variant props
* 6. User's className (overriding dynamic)
*
*
* @param variantProps - The variant props including className.
* @returns The class name.
*/
@ -180,42 +246,42 @@ export function vcn<
VariantKV<V>
>,
) => {
const { className, preset, ...otherVariantProps } = variantProps;
const { className, preset, ..._otherVariantProps } = variantProps;
const currentPreset: P[keyof P] | null =
presets && preset ? (presets as NonNullable<P>)[preset] ?? null : null;
const presetVariantKeys: (keyof V)[] = Object.keys(currentPreset ?? {});
return twMerge(
base,
...(
Object.entries(defaults) as [keyof V, keyof V[keyof V] & string][]
).map<string>(([variantKey, defaultValue]) => {
// Omit<Partial<VariantKV<V>> & { className; preset; }, className | preset> = Partial<VariantKV<V>> (safe to cast)
// Partial<VariantKV<V>>[keyof V] => { [k in keyof V]?: BooleanString<keyof V[keyof V] & string> } => BooleanString<keyof V[keyof V]>
// Omit<Partial<VariantKV<V>> & { className; preset; }, className | preset> = Partial<VariantKV<V>> (safe to cast)
// We all know `keyof V` = string, right? (but typescript says it's not, so.. attacking typescript with unknown lol)
const otherVariantProps = _otherVariantProps as unknown as Partial<
VariantKV<V>
>;
const directVariantValue: (keyof V[keyof V] & string) | undefined = (
otherVariantProps as unknown as Partial<VariantKV<V>>
)[variantKey]?.toString?.(); // BooleanString<> -> string (safe to index V[keyof V])
const kv: VariantKV<V> = { ...defaults };
const currentPresetVariantValue:
| (keyof V[keyof V] & string)
| undefined =
!!currentPreset && presetVariantKeys.includes(variantKey)
? (currentPreset as Partial<VariantKV<V>>)[
variantKey
]?.toString?.()
: undefined;
// Preset Processing
if (presets && preset && preset in presets) {
for (const [variantName, variantKey] of Object.entries(
// typescript bug (casting to NonNullable<P> required)
(presets as NonNullable<P>)[preset],
) as VariantKVEntry<V>) {
kv[variantName] = variantKey;
}
}
const variantValue: keyof V[keyof V] & string =
directVariantValue ?? currentPresetVariantValue ?? defaultValue;
return variants[variantKey][variantValue];
}),
(
currentPreset as Partial<VariantKV<V>> | null
)?.className?.toString?.(), // preset's classname comes after user's variant props? huh..
className,
);
// VariantProps Processing
for (const [variantName, variantKey] of Object.entries(
otherVariantProps,
) as VariantKVEntry<V>) {
kv[variantName] = variantKey;
}
// make dynamics result
const dynamicClasses: string[] = [];
for (const dynamicFunction of dynamics) {
dynamicClasses.push(dynamicFunction(kv));
}
return __transformer__(kv, dynamicClasses, className);
},
/**
* Takes any props, parse variant props and other props.
* If `options.excludeA` is true, then it will parse `A` as "other" props.