feat(ui): drop @nstart/ui dependency (Stage 3.9)
Все font cascade bugs из последних MR были вызваны @nstart/ui pollution
(Tektur inheritance от Card parent'ов, Onest font в Button, font-primary
class overrides). Решили обрезать раньше plan'а.
What changed:
New primitives (8 файлов в src/ui/components/):
- panel.tsx — container с border/surface
- field.tsx — FieldLabel/Hint/Error typography helpers
- text-input.tsx — Input + label/hint/error composite
- table.tsx — Table/TableHeader/Body/Row/HeaderCell/Cell/Empty
- date-picker.tsx — native <input type=date> wrapper с label/hint/error
- multi-select.tsx — Radix Popover + inline checkboxes
- single-select.tsx — native <select> с label/hint/error chrome
- language-switch.tsx — segmented control в Tektur uppercase
Compatibility shims (existing components extended):
- checkbox.tsx — + label/description/error/onChange (nstart-compat
synthetic event), wrap в <label> когда есть chrome
- modal.tsx — + panelClassName / bodyClassName props
- form-actions.tsx — + align prop (start/end/between)
- tabs-simple.tsx — TabItem.count alias for .trailing (nstart-compat)
- table TableHead — alias TableHeaderCell с align prop
Barrel rewrite (src/ui/index.ts):
- Удалён 'export * from @nstart/ui'
- Explicit exports для всех 45+ нужных символов
- SelectOption / MultiSelectOption / LanguageOption теперь {id, label}
с optional 'value' alias — nstart-compat без массового codemod call-sites
styles.css cleanup:
- Removed @import @nstart/ui/styles/{fonts,theme}.css
- Removed @source nstart/dist directive (Tailwind v4 scan)
- Removed --text-sm: 13px override (был костыль для @nstart/ui dist)
- Legacy color tokens (--color-carbon/ultramarain/orbit/regolith/asteroid)
sсодержали fallback mappings — оставлены, но callsites больше не используют
Codemod: 9 файлов конвертированы legacy colors → handoff tokens
carbon → ink, ultramarain → accent, orbit → warn, regolith → line, asteroid → mute
main.tsx:
- NStartUiProvider → TooltipProvider (Radix)
- DatePickerProvider убран (наш DatePicker native, no provider needed)
routes/dictionaries.$name.tsx:
- Removed useRef-based <input>.indeterminate hack
- Radix Checkbox принимает checked='indeterminate' declaratively
package.json: -@nstart/ui (was 0.1.3)
Bundle impact:
before: vendor-nstart-ui chunk = 1228 KB
after: no nstart chunk, всё в vendor-misc (+~70 KB Radix wrappers)
net savings: ~1.15 MB minified, ~440 KB gzip
Tests: 116 pass (1 fixed: tab role from 'button' → 'tab', Radix semantically correct).
TS strict: clean.
Browser-verified (preview): 7 utilities computed correctly, nstart bundle absent.
This commit is contained in:
@@ -0,0 +1,156 @@
|
||||
import * as React from 'react'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
/**
|
||||
* Table primitives — shadcn-style atomic wrappers. Замена @nstart/ui Table*.
|
||||
*
|
||||
* <p>Per handoff: table cell text = text-cell (12.5/500). Применяется на
|
||||
* <td/th> через CSS rule в `<tbody>` — но проще baked-in via TableCell
|
||||
* className. Caller может override через className prop.
|
||||
*
|
||||
* <p>TableEmpty — single full-width row для empty state.
|
||||
*/
|
||||
|
||||
export const Table = React.forwardRef<HTMLTableElement, React.HTMLAttributes<HTMLTableElement>>(
|
||||
function Table({ className, ...props }, ref) {
|
||||
return (
|
||||
<div className="w-full overflow-x-auto">
|
||||
<table
|
||||
ref={ref}
|
||||
className={cn('w-full caption-bottom text-cell', className)}
|
||||
{...props}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
export const TableHeader = React.forwardRef<
|
||||
HTMLTableSectionElement,
|
||||
React.HTMLAttributes<HTMLTableSectionElement>
|
||||
>(function TableHeader({ className, ...props }, ref) {
|
||||
return (
|
||||
<thead
|
||||
ref={ref}
|
||||
className={cn('[&_tr]:border-b [&_tr]:border-line', className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
})
|
||||
|
||||
export const TableBody = React.forwardRef<
|
||||
HTMLTableSectionElement,
|
||||
React.HTMLAttributes<HTMLTableSectionElement>
|
||||
>(function TableBody({ className, ...props }, ref) {
|
||||
return (
|
||||
<tbody
|
||||
ref={ref}
|
||||
className={cn('[&_tr:last-child]:border-0', className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
})
|
||||
|
||||
export const TableFooter = React.forwardRef<
|
||||
HTMLTableSectionElement,
|
||||
React.HTMLAttributes<HTMLTableSectionElement>
|
||||
>(function TableFooter({ className, ...props }, ref) {
|
||||
return (
|
||||
<tfoot
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'border-t border-line bg-surface-2 font-medium [&>tr]:last:border-b-0',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
})
|
||||
|
||||
export const TableRow = React.forwardRef<HTMLTableRowElement, React.HTMLAttributes<HTMLTableRowElement>>(
|
||||
function TableRow({ className, ...props }, ref) {
|
||||
return (
|
||||
<tr
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'border-b border-line transition-colors hover:bg-surface-2/40 data-[state=selected]:bg-accent-bg',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
export type TableHeaderCellProps = React.ThHTMLAttributes<HTMLTableCellElement> & {
|
||||
align?: 'left' | 'center' | 'right'
|
||||
}
|
||||
|
||||
export const TableHeaderCell = React.forwardRef<HTMLTableCellElement, TableHeaderCellProps>(
|
||||
function TableHeaderCell({ className, align = 'left', ...props }, ref) {
|
||||
return (
|
||||
<th
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'h-9 px-3 align-middle text-cap text-mute',
|
||||
align === 'right' && 'text-right',
|
||||
align === 'center' && 'text-center',
|
||||
align === 'left' && 'text-left',
|
||||
'[&:has([role=checkbox])]:pr-0',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
},
|
||||
)
|
||||
// Alias for shadcn convention.
|
||||
export const TableHead = TableHeaderCell
|
||||
|
||||
export const TableCell = React.forwardRef<
|
||||
HTMLTableCellElement,
|
||||
React.TdHTMLAttributes<HTMLTableCellElement>
|
||||
>(function TableCell({ className, ...props }, ref) {
|
||||
return (
|
||||
<td
|
||||
ref={ref}
|
||||
className={cn('px-3 py-2 align-middle [&:has([role=checkbox])]:pr-0', className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
})
|
||||
|
||||
export const TableCaption = React.forwardRef<
|
||||
HTMLTableCaptionElement,
|
||||
React.HTMLAttributes<HTMLTableCaptionElement>
|
||||
>(function TableCaption({ className, ...props }, ref) {
|
||||
return (
|
||||
<caption
|
||||
ref={ref}
|
||||
className={cn('mt-3 text-cell text-mute', className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
})
|
||||
|
||||
/**
|
||||
* TableEmpty — full-width row с заглушкой "ничего не найдено".
|
||||
* Замена @nstart/ui TableEmptyRow API: <TableEmpty colSpan={N}>{text}</TableEmpty>.
|
||||
*/
|
||||
export type TableEmptyProps = React.TdHTMLAttributes<HTMLTableCellElement> & {
|
||||
colSpan: number
|
||||
}
|
||||
|
||||
export function TableEmpty({ colSpan, className, children, ...props }: TableEmptyProps) {
|
||||
return (
|
||||
<tr>
|
||||
<td
|
||||
colSpan={colSpan}
|
||||
className={cn('py-10 text-center text-cell text-mute', className)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</td>
|
||||
</tr>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user