diff --git a/apps/web/src/components/settings/OpenSourceLicenses.tsx b/apps/web/src/components/settings/OpenSourceLicenses.tsx index 28932ae10ece..dc4703cad51a 100644 --- a/apps/web/src/components/settings/OpenSourceLicenses.tsx +++ b/apps/web/src/components/settings/OpenSourceLicenses.tsx @@ -5,14 +5,16 @@ import { decodeThirdPartyLicenseManifest, filterThirdPartyLicenseEntries, formatLicenseBundles, + thirdPartyLicenseEntryKey, type ThirdPartyLicenseEntry, type ThirdPartyLicenseManifest, } from "@t3tools/shared/thirdPartyLicenses"; -import { cn } from "../../lib/utils"; import { Button } from "../ui/button"; -import { Input } from "../ui/input"; -import { SettingsPageContainer } from "./settingsLayout"; +import { Collapsible, CollapsiblePanel, CollapsibleTrigger } from "../ui/collapsible"; +import { InputGroup, InputGroupAddon, InputGroupInput } from "../ui/input-group"; +import { Tooltip, TooltipPopup, TooltipTrigger } from "../ui/tooltip"; +import { SettingsPageContainer, SettingsSection } from "./settingsLayout"; type LicenseManifestState = | { readonly status: "loading" } @@ -30,80 +32,154 @@ async function loadLicenseManifest(signal: AbortSignal): Promise void; +}) { return ( -
- - {open ? ( -
-
-
-

- {entry.name} - {versionLabel} -

- {entry.sourceUrl ? ( - - Project source - - + +
+
+ + + + {entry.name} + {entry.version ? ( + {entry.version} ) : null} -
-
-              {entry.noticeText}
-            
-
+ + + {entry.license} · {formatLicenseBundles(entry.bundles)} + + + {entry.sourceUrl ? ( + + ) : null}
- ) : null} -
+ + {open ? ( +
+
+                {entry.noticeText}
+              
+
+ ) : null} +
+ + + ); +} + +function LicenseCount({ + filteredCount, + totalCount, +}: { + filteredCount: number; + totalCount: number; +}) { + return ( +

+ {filteredCount === totalCount + ? `${String(totalCount)} notices` + : `${String(filteredCount)} of ${String(totalCount)}`} +

+ ); +} + +function LicenseHeaderAction({ + query, + onQueryChange, + searchOpen, + onSearchOpenChange, + filteredCount, + totalCount, +}: { + query: string; + onQueryChange: (value: string) => void; + searchOpen: boolean; + onSearchOpenChange: (open: boolean) => void; + filteredCount: number; + totalCount: number; +}) { + if (!searchOpen) { + return ( +
+ + + onSearchOpenChange(true)} + size="icon-micro" + type="button" + variant="ghost-muted" + > + + + } + /> + Search licenses + +
+ ); + } + + return ( +
+
+ +
+ + + + + { + if (query.length === 0) onSearchOpenChange(false); + }} + onChange={(event) => onQueryChange(event.currentTarget.value)} + onKeyDown={(event) => { + if (event.key !== "Escape") return; + event.preventDefault(); + onQueryChange(""); + onSearchOpenChange(false); + }} + placeholder="Search licenses" + size="sm" + type="search" + value={query} + /> + +
); } function LicenseManifestError({ message, onRetry }: { message: string; onRetry: () => void }) { return ( -
+
-

- Open-source notices are unavailable -

-

+

Open-source notices are unavailable

+

{message}

@@ -117,6 +193,8 @@ function LicenseManifestError({ message, onRetry }: { message: string; onRetry: export function OpenSourceLicensesPanel() { const [state, setState] = useState({ status: "loading" }); const [query, setQuery] = useState(""); + const [searchOpen, setSearchOpen] = useState(false); + const [openEntryKey, setOpenEntryKey] = useState(null); const [requestVersion, setRequestVersion] = useState(0); useEffect(() => { @@ -143,63 +221,50 @@ export function OpenSourceLicensesPanel() { const retry = useCallback(() => setRequestVersion((value) => value + 1), []); return ( - -
-

- Open source licenses -

-

- Third-party notices for dependencies and assets included in T3 Code. -

-
- - {state.status === "ready" ? ( -
-
- -

- {filteredEntries.length === entries.length - ? `${String(entries.length)} notices` - : `${String(filteredEntries.length)} of ${String(entries.length)} notices`} -

-
- -
+ + + ) : null + } + > + {state.status === "ready" ? ( +
{filteredEntries.length > 0 ? ( - filteredEntries.map((entry) => ( - - )) + filteredEntries.map((entry) => { + const entryKey = thirdPartyLicenseEntryKey(entry); + return ( + setOpenEntryKey(open ? entryKey : null)} + /> + ); + }) ) : ( -

+

No licenses match that search.

)}
-
- ) : state.status === "error" ? ( - - ) : ( -

- Loading open-source notices… -

- )} + ) : state.status === "error" ? ( + + ) : ( +

+ Loading open-source notices… +

+ )} + ); }