diff options
| author | Bobby <[email protected]> | 2026-03-10 14:14:58 +0530 |
|---|---|---|
| committer | Bobby <[email protected]> | 2026-03-10 14:14:58 +0530 |
| commit | 6f2f3c013d88df6a2b37d0978194c7cb0bf1274c (patch) | |
| tree | 99746e2afbd5379c46401440e85964658bf26f25 | |
| parent | 03a9b16a0fd7cc5a293a1289646817e21663f4bd (diff) | |
| download | pagoda-6f2f3c013d88df6a2b37d0978194c7cb0bf1274c.tar.xz pagoda-6f2f3c013d88df6a2b37d0978194c7cb0bf1274c.zip | |
feat: add TypeScript support and improve type definitions across components
| -rw-r--r-- | garden/package-lock.json | 15 | ||||
| -rw-r--r-- | garden/package.json | 1 | ||||
| -rw-r--r-- | garden/src/app.tsx | 5 | ||||
| -rw-r--r-- | garden/src/components/DatePicker.tsx | 6 | ||||
| -rw-r--r-- | garden/src/components/Editor.tsx | 4 | ||||
| -rw-r--r-- | garden/src/components/Layout.tsx | 6 | ||||
| -rw-r--r-- | garden/src/index.tsx | 2 | ||||
| -rw-r--r-- | garden/src/pages/council/bannedips.tsx | 4 | ||||
| -rw-r--r-- | garden/src/pages/council/user.tsx | 14 | ||||
| -rw-r--r-- | garden/src/pages/council/users.tsx | 2 | ||||
| -rw-r--r-- | garden/src/types/custom-elements.d.ts | 4 | ||||
| -rw-r--r-- | garden/tsconfig.json | 1 |
12 files changed, 43 insertions, 21 deletions
diff --git a/garden/package-lock.json b/garden/package-lock.json index 519bdf6..6dedc78 100644 --- a/garden/package-lock.json +++ b/garden/package-lock.json @@ -24,6 +24,7 @@ "devDependencies": { "@types/node": "^25.3.2", "solid-devtools": "^0.34.3", + "typescript": "^5.9.3", "vite": "^7.1.4", "vite-plugin-solid": "^2.11.8" } @@ -2189,6 +2190,20 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, "node_modules/undici-types": { "version": "7.18.2", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", diff --git a/garden/package.json b/garden/package.json index 1072066..3435342 100644 --- a/garden/package.json +++ b/garden/package.json @@ -13,6 +13,7 @@ "devDependencies": { "@types/node": "^25.3.2", "solid-devtools": "^0.34.3", + "typescript": "^5.9.3", "vite": "^7.1.4", "vite-plugin-solid": "^2.11.8" }, diff --git a/garden/src/app.tsx b/garden/src/app.tsx index 2fb73fe..58be568 100644 --- a/garden/src/app.tsx +++ b/garden/src/app.tsx @@ -1,7 +1,8 @@ -import { Suspense, type Component } from "solid-js"; +import { Suspense } from "solid-js"; +import type { JSX } from "solid-js"; import Layout from "./components/Layout"; -const App: Component<{ children: Element }> = (props) => { +const App = (props: { children?: JSX.Element }) => { return ( <Layout> <Suspense>{props.children}</Suspense> diff --git a/garden/src/components/DatePicker.tsx b/garden/src/components/DatePicker.tsx index 8e6410d..8c914ec 100644 --- a/garden/src/components/DatePicker.tsx +++ b/garden/src/components/DatePicker.tsx @@ -121,17 +121,17 @@ export default function DatePicker(props: DatePickerProps) { </div> <div class="datepicker-weekdays"> <For each={WEEKDAYS}> - {(day) => <span class="datepicker-weekday">{day}</span>} + {(day: string) => <span class="datepicker-weekday">{day}</span>} </For> </div> <div class="datepicker-grid"> <For each={calendarDays()}> - {(day) => ( + {(day: number | null) => ( <Show when={day !== null} fallback={<span class="datepicker-blank" />}> <button type="button" class="datepicker-day" - classList={{ "datepicker-day-selected": isSelected(day!) }} + classList={{ "datepicker-day-selected": isSelected(day!) ?? undefined }} onClick={() => selectDay(day!)} > {day} diff --git a/garden/src/components/Editor.tsx b/garden/src/components/Editor.tsx index 5085032..9aa2e08 100644 --- a/garden/src/components/Editor.tsx +++ b/garden/src/components/Editor.tsx @@ -613,7 +613,7 @@ export default function Editor(props: EditorProps) { <Show when={attachments().length > 0 || pendingUploads().length > 0}> <div class="editor-attachments"> <For each={attachments()}> - {(attachment) => ( + {(attachment: AttachmentResult) => ( <div class="editor-attachment" title={attachment.file_name}> <Show when={attachment.category === "image"} fallback={ <Show when={attachment.category === "video"} fallback={ @@ -637,7 +637,7 @@ export default function Editor(props: EditorProps) { )} </For> <For each={pendingUploads()}> - {(pending) => ( + {(pending: PendingUpload) => ( <div class="editor-attachment editor-attachment-uploading" title={pending.name}> <Show when={pending.type.startsWith("image/") && pending.preview} fallback={ <Show when={pending.type.startsWith("video/") && pending.preview} fallback={ diff --git a/garden/src/components/Layout.tsx b/garden/src/components/Layout.tsx index 1580660..b3ac179 100644 --- a/garden/src/components/Layout.tsx +++ b/garden/src/components/Layout.tsx @@ -6,6 +6,8 @@ import NavSection from "./NavSection"; import { auth } from "../store/auth"; import { stats } from "../store/stats"; import { UserRole } from "../types/roles"; +import type { CitizenSummary } from "../types/stats"; + interface LayoutProps { children: JSX.Element; @@ -147,7 +149,7 @@ export default function Layout(props: LayoutProps) { <li class="placeholder">Be the first to join!</li> }> <For each={stats.data()?.newest_citizens}> - {(citizen) => ( + {(citizen: CitizenSummary) => ( <li class="citizen-item"> <A href={`/p/${citizen.username}`}> <img src={citizen.avatar_url} alt="" class="citizen-avatar" /> @@ -165,7 +167,7 @@ export default function Layout(props: LayoutProps) { <li class="placeholder">No one online.</li> }> <For each={stats.data()?.online_citizens}> - {(citizen) => ( + {(citizen: CitizenSummary) => ( <li class="citizen-item"> <A href={`/p/${citizen.username}`}> <img src={citizen.avatar_url} alt="" class="citizen-avatar" /> diff --git a/garden/src/index.tsx b/garden/src/index.tsx index 6237aa1..0bfe1b9 100644 --- a/garden/src/index.tsx +++ b/garden/src/index.tsx @@ -18,5 +18,5 @@ if (import.meta.env.DEV && !(root instanceof HTMLElement)) { render( () => <Router root={(props) => <App>{props.children}</App>}>{routes}</Router>, - root, + root!, ); diff --git a/garden/src/pages/council/bannedips.tsx b/garden/src/pages/council/bannedips.tsx index 3a94c91..f70b197 100644 --- a/garden/src/pages/council/bannedips.tsx +++ b/garden/src/pages/council/bannedips.tsx @@ -77,7 +77,7 @@ export default function BannedIPs() { <div class="council-grid-empty">No banned IPs.</div> }> <For each={bans()}> - {(ban) => ( + {(ban: IPBan) => ( <div class="council-grid-row"> <span class="council-ip">{ban.IP}</span> <span>{ban.Reason}</span> @@ -100,7 +100,7 @@ export default function BannedIPs() { </div> </Show> <Show when={confirmBan()}> - {(ban) => ( + {(ban: () => IPBan) => ( <Modal title="Lift IP Ban" onClose={() => setConfirmBan(null)}> <p style={{ "font-size": "12px", margin: "0 0 12px" }}>Lift ban on <strong>{ban().IP}</strong>?</p> <div class="modal-actions"> diff --git a/garden/src/pages/council/user.tsx b/garden/src/pages/council/user.tsx index dc445b7..0bb0c91 100644 --- a/garden/src/pages/council/user.tsx +++ b/garden/src/pages/council/user.tsx @@ -87,11 +87,11 @@ export default function CouncilUser() { } function startEdit(field: string, value: string) { - setEditing((prev) => ({ ...prev, [field]: value })); + setEditing((prev: Record<string, string>) => ({ ...prev, [field]: value })); } function cancelEdit(field: string) { - setEditing((prev) => { + setEditing((prev: Record<string, string>) => { const next = { ...prev }; delete next[field]; return next; @@ -312,7 +312,7 @@ export default function CouncilUser() { type="text" class="council-detail-edit-input" value={editing()[props.field]} - onInput={(e) => setEditing((prev) => ({ ...prev, [props.field]: e.currentTarget.value }))} + onInput={(e) => setEditing((prev: Record<string, string>) => ({ ...prev, [props.field]: e.currentTarget.value }))} onKeyDown={(e) => { if (e.key === "Enter") saveEdit(props.field); if (e.key === "Escape") cancelEdit(props.field); }} /> <button type="button" class="council-detail-edit-btn council-action-save" onClick={() => saveEdit(props.field)}>Save</button> @@ -350,10 +350,10 @@ export default function CouncilUser() { aria-label="Change role" title="Change role" value={editing()["role"]} - onChange={(e) => setEditing((prev) => ({ ...prev, role: e.currentTarget.value }))} + onChange={(e) => setEditing((prev: Record<string, string>) => ({ ...prev, role: e.currentTarget.value }))} > <For each={availableRoles()}> - {(role) => <option value={role}>{role}</option>} + {(role: UserRole) => <option value={role}>{role}</option>} </For> </select> <button type="button" class="council-detail-edit-btn council-action-save" onClick={() => saveRole(editing()["role"])}>Save</button> @@ -374,7 +374,7 @@ export default function CouncilUser() { </Show> <Show when={user()}> - {(target) => ( + {(target: () => AdminUser) => ( <> <Show when={actionError()}> <div class="form-error">{actionError()}</div> @@ -432,7 +432,7 @@ export default function CouncilUser() { type="text" class="council-detail-edit-input" value={editing()["website"]} - onInput={(event) => setEditing((prev) => ({ ...prev, website: event.currentTarget.value }))} + onInput={(event) => setEditing((prev: Record<string, string>) => ({ ...prev, website: event.currentTarget.value }))} onKeyDown={(e) => { if (e.key === "Enter") saveEdit("website"); if (e.key === "Escape") cancelEdit("website"); }} /> <button type="button" class="council-detail-edit-btn council-action-save" onClick={() => saveEdit("website")}>Save</button> diff --git a/garden/src/pages/council/users.tsx b/garden/src/pages/council/users.tsx index 317639e..1ec73db 100644 --- a/garden/src/pages/council/users.tsx +++ b/garden/src/pages/council/users.tsx @@ -66,7 +66,7 @@ export default function CouncilUsers() { <div class="council-grid-empty">No users found.</div> }> <For each={council.users()}> - {(user) => ( + {(user: AdminUser) => ( <div class="council-grid-row" onClick={() => navigate(`/council/users/${user.username}`)}> <span class="council-user-cell"> <img src={user.avatar_url} alt="" class="council-avatar" /> diff --git a/garden/src/types/custom-elements.d.ts b/garden/src/types/custom-elements.d.ts index ceaf7bb..79a10f8 100644 --- a/garden/src/types/custom-elements.d.ts +++ b/garden/src/types/custom-elements.d.ts @@ -1,7 +1,9 @@ +import "solid-js"; + declare module "solid-js" { namespace JSX { interface IntrinsicElements { - "emoji-picker": HTMLAttributes<HTMLElement> & { + "emoji-picker": JSX.HTMLAttributes<HTMLElement> & { class?: string; "on:emoji-click"?: (event: CustomEvent) => void; }; diff --git a/garden/tsconfig.json b/garden/tsconfig.json index 271eb70..f7eba49 100644 --- a/garden/tsconfig.json +++ b/garden/tsconfig.json @@ -10,6 +10,7 @@ "moduleResolution": "bundler", "noEmit": true, "strict": true, + "skipLibCheck": true, "forceConsistentCasingInFileNames": true, "types": ["vite/client"] }, |
