mirror of
https://github.com/tabler/tabler.git
synced 2026-08-27 12:36:28 +04:00
Replace remaining any casts in shared with proper types
- ui/Table.astro: type the people rows with a local Person interface
instead of Record<string, any>[]. Surfaced a latent type mismatch:
<Avatar personId={person.id}> passed a string id into a prop typed
as number (only silently worked because of the any cast) — replaced
with <Avatar person={person}>, which resolves to the same person
object without the redundant numeric re-lookup.
- ui/Select.astro: add SelectOption/SelectData types for selects.json
entries instead of two Record<string, any> casts.
- components/navbar/NavbarMenu.astro: type Level1.children as
Record<string, Level2> (matching NavbarMenuItem's own Level2/Level3
shapes) instead of Record<string, unknown>, removing the `as any`
casts at both NavbarMenuItem call sites.
- components/parts/Tasks.astro: add a Task interface instead of
Record<string, any>[]; also drops the now-redundant inline subtask
type annotation.
No behavior change other than the Table.astro Avatar fix above.
Verified: astro check 0/0/0 in shared, astro build succeeds for
preview (127 pages) and docs (128 pages), vitest passes (38 tests).
This commit is contained in:
@@ -5,6 +5,18 @@ import NavbarMenuItem from './NavbarMenuItem.astro'
|
||||
import menu from '@data/menu.json'
|
||||
import menuSample from '@data/menu-sample.json'
|
||||
|
||||
interface Level3 {
|
||||
title: string
|
||||
url?: string
|
||||
badge?: string
|
||||
}
|
||||
|
||||
interface Level2 extends Level3 {
|
||||
icon?: string
|
||||
color?: string
|
||||
children?: Record<string, Level3>
|
||||
}
|
||||
|
||||
interface Level1 {
|
||||
'title': string
|
||||
'title-long'?: string
|
||||
@@ -15,7 +27,7 @@ interface Level1 {
|
||||
'disabled'?: boolean
|
||||
'right'?: boolean
|
||||
'columns'?: number
|
||||
'children'?: Record<string, Record<string, unknown>>
|
||||
'children'?: Record<string, Level2>
|
||||
}
|
||||
|
||||
interface Props {
|
||||
@@ -80,13 +92,13 @@ const chunk = <T,>(arr: T[], size: number): T[][] => {
|
||||
{columns.map((column) => (
|
||||
<div class="dropdown-menu-column">
|
||||
{column.map(([childKey, child]) => (
|
||||
<NavbarMenuItem level1Key={key} itemKey={childKey} item={child as any} currentPage={currentPage} keepOpen={keepOpen} />
|
||||
<NavbarMenuItem level1Key={key} itemKey={childKey} item={child} currentPage={currentPage} keepOpen={keepOpen} />
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
children.map(([childKey, child]) => <NavbarMenuItem level1Key={key} itemKey={childKey} item={child as any} currentPage={currentPage} keepOpen={keepOpen} />)
|
||||
children.map(([childKey, child]) => <NavbarMenuItem level1Key={key} itemKey={childKey} item={child} currentPage={currentPage} keepOpen={keepOpen} />)
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -6,9 +6,24 @@ import AvatarList from '@ui/AvatarList.astro'
|
||||
import CardTitle from '@ui/CardTitle.astro'
|
||||
import tasksData from '@data/tasks.json'
|
||||
|
||||
interface Task {
|
||||
'name': string
|
||||
'color'?: string
|
||||
'starred'?: boolean
|
||||
'description'?: string
|
||||
'image'?: string
|
||||
'users'?: number
|
||||
'users-offset'?: number
|
||||
'due-date'?: string
|
||||
'favorite'?: boolean
|
||||
'likes'?: number
|
||||
'subtasks'?: { name: string; done?: boolean }[]
|
||||
'comments'?: number
|
||||
}
|
||||
|
||||
interface Column {
|
||||
name: string
|
||||
tasks: Record<string, any>[]
|
||||
tasks: Task[]
|
||||
}
|
||||
|
||||
interface Props {
|
||||
@@ -100,7 +115,7 @@ const { data = tasksData as unknown as { columns: Column[] }, class: className }
|
||||
|
||||
{task.subtasks && (
|
||||
<div class="divide-y-2 mt-4">
|
||||
{task.subtasks.map((subtask: { name: string; done?: boolean }) => (
|
||||
{task.subtasks.map((subtask) => (
|
||||
<div>
|
||||
{subtask.done ? (
|
||||
<Fragment>
|
||||
|
||||
+15
-2
@@ -20,6 +20,19 @@ interface Props {
|
||||
showSearch?: boolean;
|
||||
}
|
||||
|
||||
interface SelectOption {
|
||||
name: string;
|
||||
flag?: string;
|
||||
label?: string;
|
||||
selected?: boolean;
|
||||
}
|
||||
|
||||
interface SelectData {
|
||||
multiple?: boolean;
|
||||
data?: string;
|
||||
options?: string[] | { title: string; options: string[] }[] | Record<string, SelectOption>;
|
||||
}
|
||||
|
||||
const {
|
||||
id: idProp,
|
||||
key: keyProp,
|
||||
@@ -35,7 +48,7 @@ const {
|
||||
|
||||
const id = idProp ?? keyProp;
|
||||
const key = keyProp ?? 'people';
|
||||
const data = (selects as Record<string, any>)[key] ?? {};
|
||||
const data = (selects as Record<string, SelectData>)[key] ?? {};
|
||||
|
||||
const selectClass = [
|
||||
'form-select',
|
||||
@@ -78,7 +91,7 @@ if (values) {
|
||||
options = (data.options as string[]).map((option) => ({ value: option, text: option }));
|
||||
} else if (data.options) {
|
||||
// object of { key: { name, flag?, label?, selected? } } — with optional indicator
|
||||
options = Object.entries(data.options as Record<string, any>).map(([optKey, v]) => {
|
||||
options = Object.entries(data.options as Record<string, SelectOption>).map(([optKey, v]) => {
|
||||
let custom: string | undefined;
|
||||
if (indicator === 'flag') custom = `<span class="flag flag-xs flag-country-${v.flag}"></span>`;
|
||||
else if (indicator === 'label') custom = `<span class="badge bg-primary-lt">${v.label}</span>`;
|
||||
|
||||
+11
-2
@@ -6,6 +6,15 @@ import DropdownMenu from './DropdownMenu.astro'
|
||||
import people from '@data/people.json'
|
||||
import { randomNumber } from '@shared/lib/pseudo-random'
|
||||
|
||||
interface Person {
|
||||
id: string
|
||||
full_name?: string
|
||||
email?: string
|
||||
job_title?: string
|
||||
department?: string
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
interface Props {
|
||||
card?: boolean
|
||||
limit?: number
|
||||
@@ -22,7 +31,7 @@ const { card, limit = 5, stripped, offset, avatars, mobile, buttons, nowrap } =
|
||||
const roles = ['User', 'Admin', 'Owner']
|
||||
|
||||
const start = offset ?? 0
|
||||
const rows = (people as Record<string, any>[]).slice(start, start + limit)
|
||||
const rows = (people as Person[]).slice(start, start + limit)
|
||||
|
||||
const tableClasses = ['table', 'table-vcenter', mobile && 'table-mobile-md', card && 'card-table', stripped && 'table-striped', nowrap && 'table-nowrap']
|
||||
---
|
||||
@@ -52,7 +61,7 @@ const tableClasses = ['table', 'table-vcenter', mobile && 'table-mobile-md', car
|
||||
{avatars ? (
|
||||
<td data-label={mobile ? 'Name' : undefined}>
|
||||
<div class="d-flex py-1 align-items-center">
|
||||
<Avatar personId={person.id} class="me-2" />
|
||||
<Avatar person={person} class="me-2" />
|
||||
<div class="flex-fill">
|
||||
<div class="fw-medium">{person.full_name}</div>
|
||||
<div class="text-secondary">
|
||||
|
||||
Reference in New Issue
Block a user