From 4d127a038bf768eded68aae3cfa5c1b12241bdac Mon Sep 17 00:00:00 2001 From: Maze Winther Date: Fri, 27 Mar 2026 00:55:15 +0100 Subject: chore: normalize line endings --- apps/web/src/components/ui/font-picker.tsx | 516 ++++++++++++++--------------- 1 file changed, 258 insertions(+), 258 deletions(-) (limited to 'apps/web/src/components/ui/font-picker.tsx') diff --git a/apps/web/src/components/ui/font-picker.tsx b/apps/web/src/components/ui/font-picker.tsx index 77d8ab7f..016bbaf4 100644 --- a/apps/web/src/components/ui/font-picker.tsx +++ b/apps/web/src/components/ui/font-picker.tsx @@ -1,258 +1,258 @@ -"use client"; - -import { useState, useMemo, useRef, useEffect, useCallback, type CSSProperties } from "react"; -import { List, type RowComponentProps } from "react-window"; -import { - Popover, - PopoverContent, - PopoverTrigger, -} from "@/components/ui/popover"; -import { Input } from "@/components/ui/input"; -import { Button } from "@/components/ui/button"; -import { loadFullFont } from "@/lib/fonts/google-fonts"; -import { SYSTEM_FONTS } from "@/constants/font-constants"; -import type { FontAtlas, FontAtlasEntry } from "@/lib/fonts/types"; -import { useFontAtlas } from "@/hooks/use-font-atlas"; -import { cn } from "@/utils/ui"; -import { ChevronDown, Search } from "lucide-react"; -import { HugeiconsIcon } from "@hugeicons/react"; -import { TextIcon } from "@hugeicons/core-free-icons"; - -const FONT_TABS = [ - { key: "all", label: "All fonts" }, - { key: "my-fonts", label: "My fonts" }, - { key: "favorites", label: "Favorites" }, -] as const; - -type FontTab = (typeof FONT_TABS)[number]["key"]; - -const ROW_HEIGHT = 40; -const PREVIEW_SCALE = 0.8; -const LIST_WIDTH = 288; -const MAX_LIST_HEIGHT = 288; -const OVERSCAN = 15; - -interface FontPickerProps { - defaultValue?: string; - onValueChange?: (value: string) => void; - className?: string; -} - -export function FontPicker({ - defaultValue, - onValueChange, - className, -}: FontPickerProps) { - const [open, setOpen] = useState(false); - const [search, setSearch] = useState(""); - const [activeTab, setActiveTab] = useState("all"); - const searchInputRef = useRef(null); - const { atlas, status, fontNames, retry: handleRetry } = useFontAtlas({ open }); - - const filteredFonts = useMemo(() => { - if (!search) return fontNames; - const query = search.toLowerCase(); - return fontNames.filter((name) => name.toLowerCase().includes(query)); - }, [fontNames, search]); - - const listHeight = Math.min( - MAX_LIST_HEIGHT, - filteredFonts.length * ROW_HEIGHT, - ); - - const handleSelect = useCallback( - async ({ family }: { family: string }) => { - if (!SYSTEM_FONTS.has(family)) { - try { - await loadFullFont({ family }); - } catch { - // ignore load failure, font will fall back to system default - } - } - onValueChange?.(family); - setOpen(false); - }, - [onValueChange], - ); - - useEffect(() => { - if (!open) { - setSearch(""); - setActiveTab("all"); - } - }, [open]); - - const activeTabLabel = - FONT_TABS.find((t) => t.key === activeTab)?.label.toLowerCase() ?? ""; - - return ( - - -
- - - - - {defaultValue ?? "Select a font"} - -
- -
- { - event.preventDefault(); - searchInputRef.current?.focus(); - }} - onCloseAutoFocus={(event) => { - event.preventDefault(); - event.stopPropagation(); - }} - > -
- - setSearch(event.target.value)} - size="xs" - className="w-full pl-5 bg-transparent border-none! shadow-none!" - /> -
-
- {FONT_TABS.map((tab) => ( - - ))} -
- {status === "loading" && ( -
- Loading fonts... -
- )} - {status === "error" && ( -
-

- Failed to load font previews. -

- -
- )} - {status === "idle" && - fontNames.length > 0 && - filteredFonts.length === 0 && ( -
- No fonts found. -
- )} - {status === "idle" && atlas && filteredFonts.length > 0 && ( - - )} -
-
- ); -} - -function FontSpritePreview({ entry }: { entry: FontAtlasEntry }) { - return ( -
- ); -} - -type FontRowProps = { - atlas: FontAtlas; - filteredFonts: string[]; - selectedFont: string | undefined; - onFontSelect: (params: { family: string }) => void; -}; - -function FontRow({ - index, - style, - atlas, - filteredFonts, - selectedFont, - onFontSelect, -}: RowComponentProps) { - const fontName = filteredFonts[index]; - const entry = atlas.fonts[fontName]; - const isSelected = fontName === selectedFont; - const isSystemFont = SYSTEM_FONTS.has(fontName); - - return ( - - ); -} +"use client"; + +import { useState, useMemo, useRef, useEffect, useCallback, type CSSProperties } from "react"; +import { List, type RowComponentProps } from "react-window"; +import { + Popover, + PopoverContent, + PopoverTrigger, +} from "@/components/ui/popover"; +import { Input } from "@/components/ui/input"; +import { Button } from "@/components/ui/button"; +import { loadFullFont } from "@/lib/fonts/google-fonts"; +import { SYSTEM_FONTS } from "@/constants/font-constants"; +import type { FontAtlas, FontAtlasEntry } from "@/lib/fonts/types"; +import { useFontAtlas } from "@/hooks/use-font-atlas"; +import { cn } from "@/utils/ui"; +import { ChevronDown, Search } from "lucide-react"; +import { HugeiconsIcon } from "@hugeicons/react"; +import { TextIcon } from "@hugeicons/core-free-icons"; + +const FONT_TABS = [ + { key: "all", label: "All fonts" }, + { key: "my-fonts", label: "My fonts" }, + { key: "favorites", label: "Favorites" }, +] as const; + +type FontTab = (typeof FONT_TABS)[number]["key"]; + +const ROW_HEIGHT = 40; +const PREVIEW_SCALE = 0.8; +const LIST_WIDTH = 288; +const MAX_LIST_HEIGHT = 288; +const OVERSCAN = 15; + +interface FontPickerProps { + defaultValue?: string; + onValueChange?: (value: string) => void; + className?: string; +} + +export function FontPicker({ + defaultValue, + onValueChange, + className, +}: FontPickerProps) { + const [open, setOpen] = useState(false); + const [search, setSearch] = useState(""); + const [activeTab, setActiveTab] = useState("all"); + const searchInputRef = useRef(null); + const { atlas, status, fontNames, retry: handleRetry } = useFontAtlas({ open }); + + const filteredFonts = useMemo(() => { + if (!search) return fontNames; + const query = search.toLowerCase(); + return fontNames.filter((name) => name.toLowerCase().includes(query)); + }, [fontNames, search]); + + const listHeight = Math.min( + MAX_LIST_HEIGHT, + filteredFonts.length * ROW_HEIGHT, + ); + + const handleSelect = useCallback( + async ({ family }: { family: string }) => { + if (!SYSTEM_FONTS.has(family)) { + try { + await loadFullFont({ family }); + } catch { + // ignore load failure, font will fall back to system default + } + } + onValueChange?.(family); + setOpen(false); + }, + [onValueChange], + ); + + useEffect(() => { + if (!open) { + setSearch(""); + setActiveTab("all"); + } + }, [open]); + + const activeTabLabel = + FONT_TABS.find((t) => t.key === activeTab)?.label.toLowerCase() ?? ""; + + return ( + + +
+ + + + + {defaultValue ?? "Select a font"} + +
+ +
+ { + event.preventDefault(); + searchInputRef.current?.focus(); + }} + onCloseAutoFocus={(event) => { + event.preventDefault(); + event.stopPropagation(); + }} + > +
+ + setSearch(event.target.value)} + size="xs" + className="w-full pl-5 bg-transparent border-none! shadow-none!" + /> +
+
+ {FONT_TABS.map((tab) => ( + + ))} +
+ {status === "loading" && ( +
+ Loading fonts... +
+ )} + {status === "error" && ( +
+

+ Failed to load font previews. +

+ +
+ )} + {status === "idle" && + fontNames.length > 0 && + filteredFonts.length === 0 && ( +
+ No fonts found. +
+ )} + {status === "idle" && atlas && filteredFonts.length > 0 && ( + + )} +
+
+ ); +} + +function FontSpritePreview({ entry }: { entry: FontAtlasEntry }) { + return ( +
+ ); +} + +type FontRowProps = { + atlas: FontAtlas; + filteredFonts: string[]; + selectedFont: string | undefined; + onFontSelect: (params: { family: string }) => void; +}; + +function FontRow({ + index, + style, + atlas, + filteredFonts, + selectedFont, + onFontSelect, +}: RowComponentProps) { + const fontName = filteredFonts[index]; + const entry = atlas.fonts[fontName]; + const isSelected = fontName === selectedFont; + const isSystemFont = SYSTEM_FONTS.has(fontName); + + return ( + + ); +} -- cgit v1.3.1