Просмотр исходного кода

Merge branch 'master' into support/156162-166103-remark-lsx-package-biome-migration

Futa Arai 10 месяцев назад
Родитель
Сommit
8e1d86d3e8

+ 1 - 2
biome.json

@@ -30,8 +30,7 @@
       "./packages/preset-themes/**",
       "./packages/remark-attachment-refs/**",
       "./packages/remark-drawio/**",
-      "./packages/remark-growi-directive/**",
-      "./packages/ui/**"
+      "./packages/remark-growi-directive/**"
     ]
   },
   "formatter": {

+ 1 - 1
packages/ui/.eslintignore

@@ -1 +1 @@
-/dist/**
+*

+ 0 - 5
packages/ui/.eslintrc.cjs

@@ -1,5 +0,0 @@
-module.exports = {
-  extends: [
-    'weseek/react',
-  ],
-};

+ 3 - 8
packages/ui/package.json

@@ -4,14 +4,9 @@
   "description": "GROWI UI Libraries",
   "license": "MIT",
   "private": "true",
-  "keywords": [
-    "growi"
-  ],
+  "keywords": ["growi"],
   "type": "module",
-  "files": [
-    "dist",
-    "scss"
-  ],
+  "files": ["dist", "scss"],
   "exports": {
     "./dist/components": {
       "import": "./dist/components/index.js"
@@ -32,7 +27,7 @@
     "clean": "shx rm -rf dist",
     "dev": "vite build --mode dev",
     "watch": "pnpm run dev -w --emptyOutDir=false",
-    "lint:js": "eslint **/*.{js,ts}",
+    "lint:js": "biome check",
     "lint:styles": "stylelint \"./scss/**/*\"",
     "lint:typecheck": "vue-tsc --noEmit",
     "lint": "npm-run-all -p lint:*"

+ 46 - 28
packages/ui/src/components/Attachment.tsx

@@ -6,17 +6,15 @@ import { format } from 'date-fns/format';
 import { UserPicture } from './UserPicture';
 
 type AttachmentProps = {
-  attachment: IAttachmentHasId,
-  inUse: boolean,
-  onAttachmentDeleteClicked?: (attachment: IAttachmentHasId) => void,
-  isUserLoggedIn?: boolean,
+  attachment: IAttachmentHasId;
+  inUse: boolean;
+  onAttachmentDeleteClicked?: (attachment: IAttachmentHasId) => void;
+  isUserLoggedIn?: boolean;
 };
 
 export const Attachment = (props: AttachmentProps): JSX.Element => {
-
-  const {
-    attachment, inUse, isUserLoggedIn, onAttachmentDeleteClicked,
-  } = props;
+  const { attachment, inUse, isUserLoggedIn, onAttachmentDeleteClicked } =
+    props;
 
   const _onAttachmentDeleteClicked = () => {
     if (onAttachmentDeleteClicked != null) {
@@ -24,23 +22,37 @@ export const Attachment = (props: AttachmentProps): JSX.Element => {
     }
   };
 
-  const formatIcon = (attachment.fileFormat.match(/image\/.+/i)) ? 'image' : 'description';
-  const btnDownload = (isUserLoggedIn)
-    ? (
-      <a className="attachment-download" href={attachment.downloadPathProxied}>
-        <span className="material-symbols-outlined">cloud_download</span>
-      </a>
-    )
-    : '';
-  const btnTrash = (isUserLoggedIn)
-    ? (
-      <a className="text-danger attachment-delete" onClick={_onAttachmentDeleteClicked}>
-        <span className="material-symbols-outlined">delete</span>
-      </a>
-    )
-    : '';
-  const fileType = <span className="attachment-filetype badge bg-secondary rounded-pill">{attachment.fileFormat}</span>;
-  const fileInUse = (inUse) ? <span className="attachment-in-use badge bg-info rounded-pill">In Use</span> : '';
+  const formatIcon = attachment.fileFormat.match(/image\/.+/i)
+    ? 'image'
+    : 'description';
+  const btnDownload = isUserLoggedIn ? (
+    <a className="attachment-download" href={attachment.downloadPathProxied}>
+      <span className="material-symbols-outlined">cloud_download</span>
+    </a>
+  ) : (
+    ''
+  );
+  const btnTrash = isUserLoggedIn ? (
+    <button
+      className="text-danger attachment-delete btn btn-link p-0"
+      onClick={_onAttachmentDeleteClicked}
+      type="button"
+    >
+      <span className="material-symbols-outlined">delete</span>
+    </button>
+  ) : (
+    ''
+  );
+  const fileType = (
+    <span className="attachment-filetype badge bg-secondary rounded-pill">
+      {attachment.fileFormat}
+    </span>
+  );
+  const fileInUse = inUse ? (
+    <span className="attachment-in-use badge bg-info rounded-pill">In Use</span>
+  ) : (
+    ''
+  );
   // Should UserDate be used like PageRevisionTable ?
   const formatType = 'yyyy/MM/dd HH:mm:ss';
   const createdAt = format(new Date(attachment.createdAt), formatType);
@@ -48,10 +60,16 @@ export const Attachment = (props: AttachmentProps): JSX.Element => {
   return (
     <div className="attachment mb-2">
       <span className="me-1 attachment-userpicture">
-        <UserPicture user={attachment.creator} size="sm"></UserPicture>
+        <UserPicture user={attachment.creator} size="sm" />
       </span>
-      <a className="me-2" href={attachment.filePathProxied} target="_blank" rel="noopener noreferrer">
-        <span className="material-symbols-outlined ms-1">{formatIcon}</span> {attachment.originalName}
+      <a
+        className="me-2"
+        href={attachment.filePathProxied}
+        target="_blank"
+        rel="noopener noreferrer"
+      >
+        <span className="material-symbols-outlined ms-1">{formatIcon}</span>{' '}
+        {attachment.originalName}
       </a>
       <span className="me-2">{fileType}</span>
       <span className="me-2">{createdAt}</span>

+ 8 - 2
packages/ui/src/components/LoadingSpinner.tsx

@@ -4,6 +4,12 @@ import styles from './LoadingSpinner.module.scss';
 
 const moduleClass = styles.spinner ?? '';
 
-export const LoadingSpinner = ({ className = '' }: ComponentPropsWithoutRef<'span'>): JSX.Element => (
-  <span className={`material-symbols-outlined pb-0 ${moduleClass} ${className}`}>progress_activity</span>
+export const LoadingSpinner = ({
+  className = '',
+}: ComponentPropsWithoutRef<'span'>): JSX.Element => (
+  <span
+    className={`material-symbols-outlined pb-0 ${moduleClass} ${className}`}
+  >
+    progress_activity
+  </span>
 );

+ 67 - 38
packages/ui/src/components/PagePath/PageListMeta.tsx

@@ -1,99 +1,125 @@
 import type { FC, JSX } from 'react';
 
-import assert from 'assert';
-
 import type { IPageHasId } from '@growi/core';
-import { templateChecker, pagePathUtils } from '@growi/core/dist/utils';
-
+import { pagePathUtils, templateChecker } from '@growi/core/dist/utils';
 
 const { isTopPage } = pagePathUtils;
 const { checkTemplatePath } = templateChecker;
 
-
 const SEEN_USERS_HIDE_THRES__ACTIVE_USERS_COUNT = 5;
 const MAX_STRENGTH_LEVEL = 4;
 
 type SeenUsersCountProps = {
-  count: number,
-  basisViewersCount?: number,
-  shouldSpaceOutIcon?: boolean,
-}
+  count: number;
+  basisViewersCount?: number;
+  shouldSpaceOutIcon?: boolean;
+};
 
 const SeenUsersCount = (props: SeenUsersCountProps): JSX.Element => {
-
   const { count, shouldSpaceOutIcon, basisViewersCount } = props;
 
   if (count === 0) {
     return <></>;
   }
 
-  if (basisViewersCount != null && basisViewersCount <= SEEN_USERS_HIDE_THRES__ACTIVE_USERS_COUNT) {
+  if (
+    basisViewersCount != null &&
+    basisViewersCount <= SEEN_USERS_HIDE_THRES__ACTIVE_USERS_COUNT
+  ) {
     return <></>;
   }
 
   const strengthLevel = Math.ceil(
-    Math.min(0, Math.log(count / (basisViewersCount ?? count))) // Max: 0
-    * 2 * -1,
+    Math.min(0, Math.log(count / (basisViewersCount ?? count))) * // Max: 0
+      2 *
+      -1,
   );
 
   if (strengthLevel > MAX_STRENGTH_LEVEL) {
     return <></>;
   }
 
-  assert(strengthLevel >= 0 && strengthLevel <= MAX_STRENGTH_LEVEL); // [0, MAX_STRENGTH_LEVEL)
+  if (!(strengthLevel >= 0 && strengthLevel <= MAX_STRENGTH_LEVEL)) {
+    throw new Error('strengthLevel out of range');
+  } // [0, MAX_STRENGTH_LEVEL)
 
   const strengthClass = `strength-${strengthLevel}`; // strength-{0, 1, 2, 3, 4}
 
   return (
-    <span className={`seen-users-count ${shouldSpaceOutIcon ? 'me-2' : ''} ${strengthClass}`}>
+    <span
+      className={`seen-users-count ${shouldSpaceOutIcon ? 'me-2' : ''} ${strengthClass}`}
+    >
       <span className="material-symbols-outlined">footprint</span>
       {count}
     </span>
   );
-
 };
 
-
 type PageListMetaProps = {
-  page: IPageHasId,
-  likerCount?: number,
-  bookmarkCount?: number,
-  shouldSpaceOutIcon?: boolean,
-  basisViewersCount?: number,
-}
-
-export const PageListMeta: FC<PageListMetaProps> = (props: PageListMetaProps) => {
+  page: IPageHasId;
+  likerCount?: number;
+  bookmarkCount?: number;
+  shouldSpaceOutIcon?: boolean;
+  basisViewersCount?: number;
+};
 
+export const PageListMeta: FC<PageListMetaProps> = (
+  props: PageListMetaProps,
+) => {
   const { page, shouldSpaceOutIcon, basisViewersCount } = props;
 
   // top check
-  let topLabel;
+  let topLabel: JSX.Element | undefined;
   if (isTopPage(page.path)) {
-    topLabel = <span className={`badge bg-info ${shouldSpaceOutIcon ? 'me-2' : ''} top-label`}>TOP</span>;
+    topLabel = (
+      <span
+        className={`badge bg-info ${shouldSpaceOutIcon ? 'me-2' : ''} top-label`}
+      >
+        TOP
+      </span>
+    );
   }
 
   // template check
-  let templateLabel;
+  let templateLabel: JSX.Element | undefined;
   if (checkTemplatePath(page.path)) {
-    templateLabel = <span className={`badge bg-info ${shouldSpaceOutIcon ? 'me-2' : ''}`}>TMPL</span>;
+    templateLabel = (
+      <span className={`badge bg-info ${shouldSpaceOutIcon ? 'me-2' : ''}`}>
+        TMPL
+      </span>
+    );
   }
 
-  let commentCount;
+  let commentCount: JSX.Element | undefined;
   if (page.commentCount > 0) {
-    commentCount = <span className={`${shouldSpaceOutIcon ? 'me-2' : ''}`}><span className="material-symbols-outlined">comment</span>{page.commentCount}</span>;
+    commentCount = (
+      <span className={`${shouldSpaceOutIcon ? 'me-2' : ''}`}>
+        <span className="material-symbols-outlined">comment</span>
+        {page.commentCount}
+      </span>
+    );
   }
 
-  let likerCount;
+  let likerCount: JSX.Element | undefined;
   if (props.likerCount != null && props.likerCount > 0) {
-    likerCount = <span className={`${shouldSpaceOutIcon ? 'me-2' : ''}`}><span className="material-symbols-outlined">favorite</span>{props.likerCount}</span>;
+    likerCount = (
+      <span className={`${shouldSpaceOutIcon ? 'me-2' : ''}`}>
+        <span className="material-symbols-outlined">favorite</span>
+        {props.likerCount}
+      </span>
+    );
   }
 
-  let locked;
+  let locked: JSX.Element | undefined;
   if (page.grant !== 1) {
-    locked = <span className={`${shouldSpaceOutIcon ? 'me-2' : ''}`}><span className="material-symbols-outlined">lock</span></span>;
+    locked = (
+      <span className={`${shouldSpaceOutIcon ? 'me-2' : ''}`}>
+        <span className="material-symbols-outlined">lock</span>
+      </span>
+    );
   }
 
-  let bookmarkCount;
+  let bookmarkCount: JSX.Element | undefined;
   if (props.bookmarkCount != null && props.bookmarkCount > 0) {
     bookmarkCount = (
       <span className={`${shouldSpaceOutIcon ? 'me-2' : ''}`}>
@@ -107,12 +133,15 @@ export const PageListMeta: FC<PageListMetaProps> = (props: PageListMetaProps) =>
     <span className="page-list-meta">
       {topLabel}
       {templateLabel}
-      <SeenUsersCount count={page.seenUsers.length} basisViewersCount={basisViewersCount} shouldSpaceOutIcon={shouldSpaceOutIcon} />
+      <SeenUsersCount
+        count={page.seenUsers.length}
+        basisViewersCount={basisViewersCount}
+        shouldSpaceOutIcon={shouldSpaceOutIcon}
+      />
       {commentCount}
       {likerCount}
       {locked}
       {bookmarkCount}
     </span>
   );
-
 };

+ 42 - 31
packages/ui/src/components/PagePath/PagePathLabel.tsx

@@ -2,54 +2,65 @@ import type { FC, ReactNode } from 'react';
 
 import { DevidedPagePath } from '@growi/core/dist/models';
 
-
 type TextElemProps = {
-  children?: ReactNode
-  isHTML?: boolean,
-}
+  children?: ReactNode;
+  isHTML?: boolean;
+};
 
 const TextElement: FC<TextElemProps> = (props: TextElemProps) => (
   <>
-    { props.isHTML
-      // eslint-disable-next-line react/no-danger
-      ? <span dangerouslySetInnerHTML={{ __html: props.children?.toString() || '' }}></span>
-      : <>{props.children}</>
-    }
+    {props.isHTML ? (
+      <span
+        // biome-ignore lint/security/noDangerouslySetInnerHtml: ignore
+        dangerouslySetInnerHTML={{ __html: props.children?.toString() || '' }}
+      />
+    ) : (
+      <>{props.children}</>
+    )}
   </>
 );
 
-
 type Props = {
-  path: string,
-  isLatterOnly?: boolean,
-  isFormerOnly?: boolean,
-  isPathIncludedHtml?: boolean,
-  additionalClassNames?: string[],
-}
-
-export const PagePathLabel: FC<Props> = (props:Props) => {
+  path: string;
+  isLatterOnly?: boolean;
+  isFormerOnly?: boolean;
+  isPathIncludedHtml?: boolean;
+  additionalClassNames?: string[];
+};
+
+export const PagePathLabel: FC<Props> = (props: Props) => {
   const {
-    isLatterOnly, isFormerOnly, isPathIncludedHtml, additionalClassNames, path,
+    isLatterOnly,
+    isFormerOnly,
+    isPathIncludedHtml,
+    additionalClassNames,
+    path,
   } = props;
 
   const dPagePath = new DevidedPagePath(path, false, true);
 
   const classNames = additionalClassNames || [];
 
-  let textElem;
+  let textElem: JSX.Element | undefined;
 
   if (isLatterOnly) {
-    textElem = <TextElement isHTML={isPathIncludedHtml}>{dPagePath.latter}</TextElement>;
-  }
-  else if (isFormerOnly) {
-    textElem = dPagePath.isFormerRoot
-      ? <>/</>
-      : <TextElement isHTML={isPathIncludedHtml}>{dPagePath.former}</TextElement>;
-  }
-  else {
-    textElem = dPagePath.isRoot
-      ? <strong>/</strong>
-      : <TextElement isHTML={isPathIncludedHtml}>{dPagePath.former}/<strong>{dPagePath.latter}</strong></TextElement>;
+    textElem = (
+      <TextElement isHTML={isPathIncludedHtml}>{dPagePath.latter}</TextElement>
+    );
+  } else if (isFormerOnly) {
+    textElem = dPagePath.isFormerRoot ? (
+      <>/</>
+    ) : (
+      <TextElement isHTML={isPathIncludedHtml}>{dPagePath.former}</TextElement>
+    );
+  } else {
+    textElem = dPagePath.isRoot ? (
+      <strong>/</strong>
+    ) : (
+      <TextElement isHTML={isPathIncludedHtml}>
+        {dPagePath.former}/<strong>{dPagePath.latter}</strong>
+      </TextElement>
+    );
   }
 
   return <span className={classNames.join(' ')}>{textElem}</span>;

+ 80 - 34
packages/ui/src/components/UserPicture.tsx

@@ -1,9 +1,13 @@
 import {
-  type ReactNode, type JSX,
-  memo, forwardRef, useCallback, useRef,
+  type JSX,
+  type ReactNode,
+  forwardRef,
+  memo,
+  useCallback,
+  useRef,
 } from 'react';
 
-import type { Ref, IUser } from '@growi/core';
+import type { IUser, Ref } from '@growi/core';
 import { pagePathUtils } from '@growi/core/dist/utils';
 import dynamic from 'next/dynamic';
 import { useRouter } from 'next/router';
@@ -14,31 +18,43 @@ import styles from './UserPicture.module.scss';
 const moduleClass = styles['user-picture'];
 const moduleTooltipClass = styles['user-picture-tooltip'];
 
-const UncontrolledTooltip = dynamic<UncontrolledTooltipProps>(() => import('reactstrap').then(mod => mod.UncontrolledTooltip), { ssr: false });
+const UncontrolledTooltip = dynamic<UncontrolledTooltipProps>(
+  () => import('reactstrap').then((mod) => mod.UncontrolledTooltip),
+  { ssr: false },
+);
 
 const DEFAULT_IMAGE = '/images/icons/user.svg';
 
-
 type UserPictureSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
 
 type BaseUserPictureRootProps = {
-  displayName: string,
-  children: ReactNode,
-  size?: UserPictureSize,
-  className?: string,
-}
+  displayName: string;
+  children: ReactNode;
+  size?: UserPictureSize;
+  className?: string;
+};
 
 type UserPictureRootWithoutLinkProps = BaseUserPictureRootProps;
 
 type UserPictureRootWithLinkProps = BaseUserPictureRootProps & {
-  username: string,
-}
+  username: string;
+};
 
-const UserPictureRootWithoutLink = forwardRef<HTMLSpanElement, UserPictureRootWithoutLinkProps>((props, ref) => {
-  return <span ref={ref} className={props.className}>{props.children}</span>;
+const UserPictureRootWithoutLink = forwardRef<
+  HTMLSpanElement,
+  UserPictureRootWithoutLinkProps
+>((props, ref) => {
+  return (
+    <span ref={ref} className={props.className}>
+      {props.children}
+    </span>
+  );
 });
 
-const UserPictureRootWithLink = forwardRef<HTMLSpanElement, UserPictureRootWithLinkProps>((props, ref) => {
+const UserPictureRootWithLink = forwardRef<
+  HTMLSpanElement,
+  UserPictureRootWithLinkProps
+>((props, ref) => {
   const router = useRouter();
 
   const { username } = props;
@@ -51,14 +67,27 @@ const UserPictureRootWithLink = forwardRef<HTMLSpanElement, UserPictureRootWithL
   // Using <span> tag here instead of <a> tag because UserPicture is used in SearchResultList which is essentially a anchor tag.
   // Nested anchor tags causes a warning.
   // https://stackoverflow.com/questions/13052598/creating-anchor-tag-inside-anchor-taga
-  return <span ref={ref} className={props.className} onClick={clickHandler} style={{ cursor: 'pointer' }}>{props.children}</span>;
+  return (
+    <span
+      ref={ref}
+      className={props.className}
+      onClick={clickHandler}
+      onKeyDown={() => {}}
+      style={{ cursor: 'pointer' }}
+    >
+      {props.children}
+    </span>
+  );
 });
 
-
 // wrapper with Tooltip
-const withTooltip = <P extends BaseUserPictureRootProps>(
-  UserPictureSpanElm: React.ForwardRefExoticComponent<P & React.RefAttributes<HTMLSpanElement>>,
-) => (props: P): JSX.Element => {
+const withTooltip =
+  <P extends BaseUserPictureRootProps>(
+    UserPictureSpanElm: React.ForwardRefExoticComponent<
+      P & React.RefAttributes<HTMLSpanElement>
+    >,
+  ) =>
+  (props: P): JSX.Element => {
     const { displayName, size } = props;
     const username = 'username' in props ? props.username : undefined;
 
@@ -75,53 +104,68 @@ const withTooltip = <P extends BaseUserPictureRootProps>(
           delay={0}
           fade={false}
         >
-          {username ? <>{`@${username}`}<br /></> : null}
+          {username ? (
+            <>
+              {`@${username}`}
+              <br />
+            </>
+          ) : null}
           {displayName}
         </UncontrolledTooltip>
       </>
     );
   };
 
-
 /**
  * type guard to determine whether the specified object is IUser
  */
-const hasUsername = (obj: Partial<IUser> | Ref<IUser> | null | undefined): obj is { username: string } => {
+const hasUsername = (
+  obj: Partial<IUser> | Ref<IUser> | null | undefined,
+): obj is { username: string } => {
   return obj != null && typeof obj !== 'string' && 'username' in obj;
 };
 
 /**
  * Type guard to determine whether tooltip should be shown
  */
-const hasName = (obj: Partial<IUser> | Ref<IUser> | null | undefined): obj is { name: string } => {
+const hasName = (
+  obj: Partial<IUser> | Ref<IUser> | null | undefined,
+): obj is { name: string } => {
   return obj != null && typeof obj === 'object' && 'name' in obj;
 };
 
 /**
  * type guard to determine whether the specified object is IUser
  */
-const hasProfileImage = (obj: Partial<IUser> | Ref<IUser> | null | undefined): obj is { imageUrlCached: string } => {
+const hasProfileImage = (
+  obj: Partial<IUser> | Ref<IUser> | null | undefined,
+): obj is { imageUrlCached: string } => {
   return obj != null && typeof obj === 'object' && 'imageUrlCached' in obj;
 };
 
-
 type Props = {
-  user?: Partial<IUser> | Ref<IUser> | null,
-  size?: UserPictureSize,
-  noLink?: boolean,
-  noTooltip?: boolean,
-  className?: string
+  user?: Partial<IUser> | Ref<IUser> | null;
+  size?: UserPictureSize;
+  noLink?: boolean;
+  noTooltip?: boolean;
+  className?: string;
 };
 
 export const UserPicture = memo((userProps: Props): JSX.Element => {
   const {
-    user, size, noLink, noTooltip, className: additionalClassName,
+    user,
+    size,
+    noLink,
+    noTooltip,
+    className: additionalClassName,
   } = userProps;
 
   // Extract user information
   const username = hasUsername(user) ? user.username : undefined;
   const displayName = hasName(user) ? user.name : 'someone';
-  const src = hasProfileImage(user) ? user.imageUrlCached ?? DEFAULT_IMAGE : DEFAULT_IMAGE;
+  const src = hasProfileImage(user)
+    ? (user.imageUrlCached ?? DEFAULT_IMAGE)
+    : DEFAULT_IMAGE;
   const showTooltip = !noTooltip && hasName(user);
 
   // Build className
@@ -131,7 +175,9 @@ export const UserPicture = memo((userProps: Props): JSX.Element => {
     'rounded-circle',
     size && `user-picture-${size}`,
     additionalClassName,
-  ].filter(Boolean).join(' ');
+  ]
+    .filter(Boolean)
+    .join(' ');
 
   const imgElement = <img src={src} alt={displayName} className={className} />;
   const baseProps = { displayName, size, children: imgElement };

+ 1 - 1
packages/ui/src/interfaces/breakpoints.ts

@@ -6,4 +6,4 @@ export const Breakpoint = {
   XL: 'xl',
   XXL: 'xxl',
 } as const;
-export type Breakpoint = typeof Breakpoint[keyof typeof Breakpoint];
+export type Breakpoint = (typeof Breakpoint)[keyof typeof Breakpoint];

+ 4 - 4
packages/ui/src/interfaces/popper-data.ts

@@ -1,8 +1,8 @@
 interface Rect {
-  top: number
-  left: number
-  width: number
-  height: number
+  top: number;
+  left: number;
+  width: number;
+  height: number;
 }
 
 export interface PopperData {

+ 12 - 7
packages/ui/src/utils/browser-utils.ts

@@ -3,12 +3,17 @@ import type { Breakpoint } from '../interfaces/breakpoints';
 const EVENT_TYPE_CHANGE = 'change';
 
 export const addBreakpointListener = (
-    breakpoint: Breakpoint,
-    // eslint-disable-next-line @typescript-eslint/no-explicit-any
-    listener: (this: MediaQueryList, ev: MediaQueryListEvent) => any,
+  breakpoint: Breakpoint,
+  // biome-ignore lint/suspicious/noExplicitAny: ignore
+  listener: (this: MediaQueryList, ev: MediaQueryListEvent) => any,
 ): MediaQueryList => {
   // get the value of '--bs-breakpoint-*'
-  const breakpointPixel = parseInt(window.getComputedStyle(document.documentElement).getPropertyValue(`--bs-breakpoint-${breakpoint}`), 10);
+  const breakpointPixel = Number.parseInt(
+    window
+      .getComputedStyle(document.documentElement)
+      .getPropertyValue(`--bs-breakpoint-${breakpoint}`),
+    10,
+  );
 
   const mediaQueryList = window.matchMedia(`(min-width: ${breakpointPixel}px)`);
 
@@ -19,9 +24,9 @@ export const addBreakpointListener = (
 };
 
 export const cleanupBreakpointListener = (
-    mediaQueryList: MediaQueryList,
-    // eslint-disable-next-line @typescript-eslint/no-explicit-any
-    listener: (this: MediaQueryList, ev: MediaQueryListEvent) => any,
+  mediaQueryList: MediaQueryList,
+  // biome-ignore lint/suspicious/noExplicitAny: ignore
+  listener: (this: MediaQueryList, ev: MediaQueryListEvent) => any,
 ): void => {
   mediaQueryList.removeEventListener(EVENT_TYPE_CHANGE, listener);
 };

+ 1 - 3
packages/ui/src/utils/use-fullscreen.ts

@@ -1,6 +1,4 @@
-import {
-  useCallback, useEffect, useMemo, useState,
-} from 'react';
+import { useCallback, useEffect, useMemo, useState } from 'react';
 
 export interface FullScreenHandle {
   active: boolean;

+ 7 - 9
packages/ui/src/utils/use-rect.ts

@@ -1,20 +1,18 @@
 // based on https://gist.github.com/morajabi/523d7a642d8c0a2f71fcfa0d8b3d2846?permalink_comment_id=4688158#gistcomment-4688158
 
 import type { RefObject } from 'react';
-import {
-  useState, useEffect, useCallback,
-} from 'react';
+import { useCallback, useEffect, useState } from 'react';
 
 type MutableRefObject<T> = {
-  current: T
-}
+  current: T;
+};
 
-type EventType = 'resize' | 'scroll'
+type EventType = 'resize' | 'scroll';
 
 const useEffectInEvent = (
-    event: EventType,
-    useCapture?: boolean,
-    set?: () => void,
+  event: EventType,
+  useCapture?: boolean,
+  set?: () => void,
 ) => {
   useEffect(() => {
     if (set) {

+ 1 - 3
packages/ui/tsconfig.json

@@ -9,7 +9,5 @@
       "~/*": ["./src/*"]
     }
   },
-  "include": [
-    "src"
-  ]
+  "include": ["src"]
 }

+ 1 - 1
packages/ui/vite.config.ts

@@ -1,4 +1,4 @@
-import path from 'path';
+import path from 'node:path';
 
 import react from '@vitejs/plugin-react';
 import glob from 'glob';