mirror of
https://github.com/MAKS-IT-COM/maksit-webui.git
synced 2026-08-15 09:58:09 +02:00
(refactor): Breadcrumb uses react-router Link; drop linkComponent injection
Some checks failed
Storybook tests / storybook-tests (push) Has been cancelled
Some checks failed
Storybook tests / storybook-tests (push) Has been cancelled
This commit is contained in:
parent
55611f8c3b
commit
cd575eb926
10
CHANGELOG.md
10
CHANGELOG.md
@ -4,6 +4,16 @@ All notable changes to this project will be documented in this file.
|
|||||||
|
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [0.4.5] - 2026-08-12
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- **Breaking:** `Breadcrumb` now renders links with react-router `Link` directly. Dropped injectable `linkComponent`, `href`, and `linkProps` — use `to` on trail items only. Default link/current/separator colors use slate tones.
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
|
||||||
|
- `BreadcrumbLinkComponent` export (no longer needed).
|
||||||
|
|
||||||
## [0.4.4] - 2026-08-12
|
## [0.4.4] - 2026-08-12
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|||||||
@ -1,30 +1,12 @@
|
|||||||
import {
|
import { type FC, type ReactNode } from 'react'
|
||||||
type ComponentType,
|
import { Link } from 'react-router-dom'
|
||||||
type FC,
|
|
||||||
type ReactNode,
|
|
||||||
} from 'react'
|
|
||||||
|
|
||||||
export interface BreadcrumbItem {
|
export interface BreadcrumbItem {
|
||||||
label: ReactNode
|
label: ReactNode
|
||||||
/** Used by default `<a>` and as fallback for router links. */
|
/** Omit on the current page (last item). */
|
||||||
href?: string
|
|
||||||
/** Preferred when injecting react-router `Link`. */
|
|
||||||
to?: string
|
to?: string
|
||||||
/** Extra props passed to the injected link component. */
|
|
||||||
linkProps?: Record<string, unknown>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Injectable link surface for SPA routers.
|
|
||||||
* `to` is always provided when Breadcrumb renders a link, so react-router `Link` is assignable.
|
|
||||||
*/
|
|
||||||
export type BreadcrumbLinkComponent = ComponentType<{
|
|
||||||
to: string
|
|
||||||
href?: string
|
|
||||||
className?: string
|
|
||||||
children?: ReactNode
|
|
||||||
}>
|
|
||||||
|
|
||||||
export interface BreadcrumbProps {
|
export interface BreadcrumbProps {
|
||||||
items: BreadcrumbItem[]
|
items: BreadcrumbItem[]
|
||||||
/** Defaults to `"/"`. */
|
/** Defaults to `"/"`. */
|
||||||
@ -33,35 +15,21 @@ export interface BreadcrumbProps {
|
|||||||
linkClassName?: string
|
linkClassName?: string
|
||||||
currentClassName?: string
|
currentClassName?: string
|
||||||
separatorClassName?: string
|
separatorClassName?: string
|
||||||
/** Host injects `Link` from react-router (or any anchor-like component). Defaults to `<a>`. */
|
|
||||||
linkComponent?: BreadcrumbLinkComponent
|
|
||||||
/** Accessible name for the nav landmark. Defaults to `"Breadcrumb"`. */
|
/** Accessible name for the nav landmark. Defaults to `"Breadcrumb"`. */
|
||||||
label?: string
|
label?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const DefaultLink: BreadcrumbLinkComponent = ({
|
|
||||||
href,
|
|
||||||
to,
|
|
||||||
children,
|
|
||||||
...rest
|
|
||||||
}) => (
|
|
||||||
<a href={href ?? to} {...rest}>
|
|
||||||
{children}
|
|
||||||
</a>
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Presentational page trail. Use links/`span` only — never headings.
|
* Page trail (`nav` + `ol`). Links use react-router `Link`.
|
||||||
* Keep a single page `h1` on `FormHeader` or the page title.
|
* Never uses headings — keep a single page `h1` on `FormHeader` or the page title.
|
||||||
*/
|
*/
|
||||||
const Breadcrumb: FC<BreadcrumbProps> = ({
|
const Breadcrumb: FC<BreadcrumbProps> = ({
|
||||||
items,
|
items,
|
||||||
separator = '/',
|
separator = '/',
|
||||||
className = '',
|
className = '',
|
||||||
linkClassName = 'text-sky-700 hover:text-sky-900 hover:underline',
|
linkClassName = 'text-slate-500 hover:text-slate-800 hover:underline',
|
||||||
currentClassName = 'text-gray-700',
|
currentClassName = 'text-slate-700',
|
||||||
separatorClassName = 'text-gray-400',
|
separatorClassName = 'text-slate-400',
|
||||||
linkComponent: LinkComponent = DefaultLink,
|
|
||||||
label = 'Breadcrumb',
|
label = 'Breadcrumb',
|
||||||
}) => {
|
}) => {
|
||||||
if (items.length === 0)
|
if (items.length === 0)
|
||||||
@ -75,8 +43,7 @@ const Breadcrumb: FC<BreadcrumbProps> = ({
|
|||||||
<ol className={'flex flex-wrap items-center gap-x-2 gap-y-1'}>
|
<ol className={'flex flex-wrap items-center gap-x-2 gap-y-1'}>
|
||||||
{items.map((item, index) => {
|
{items.map((item, index) => {
|
||||||
const isLast = index === items.length - 1
|
const isLast = index === items.length - 1
|
||||||
const target = item.to ?? item.href
|
const isLink = !isLast && Boolean(item.to)
|
||||||
const isLink = !isLast && Boolean(target)
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<li
|
<li
|
||||||
@ -92,15 +59,13 @@ const Breadcrumb: FC<BreadcrumbProps> = ({
|
|||||||
</span>
|
</span>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{isLink && target ? (
|
{isLink && item.to ? (
|
||||||
<LinkComponent
|
<Link
|
||||||
href={item.href ?? (typeof item.to === 'string' ? item.to : undefined)}
|
to={item.to}
|
||||||
to={target}
|
|
||||||
className={linkClassName}
|
className={linkClassName}
|
||||||
{...(item.linkProps ?? {})}
|
|
||||||
>
|
>
|
||||||
{item.label}
|
{item.label}
|
||||||
</LinkComponent>
|
</Link>
|
||||||
) : (
|
) : (
|
||||||
<span
|
<span
|
||||||
className={currentClassName}
|
className={currentClassName}
|
||||||
|
|||||||
@ -1,6 +1,2 @@
|
|||||||
export { Breadcrumb } from './Breadcrumb'
|
export { Breadcrumb } from './Breadcrumb'
|
||||||
export type {
|
export type { BreadcrumbProps, BreadcrumbItem } from './Breadcrumb'
|
||||||
BreadcrumbProps,
|
|
||||||
BreadcrumbItem,
|
|
||||||
BreadcrumbLinkComponent,
|
|
||||||
} from './Breadcrumb'
|
|
||||||
|
|||||||
@ -4,11 +4,7 @@ export { SecretComponent } from './components/editors/SecretComponent'
|
|||||||
export type { SecretDataSource, SecretComponentProps } from './components/editors/SecretComponent'
|
export type { SecretDataSource, SecretComponentProps } from './components/editors/SecretComponent'
|
||||||
export { FormContainer, FormContent, FormFooter, FormHeader } from './components/FormLayout'
|
export { FormContainer, FormContent, FormFooter, FormHeader } from './components/FormLayout'
|
||||||
export { Breadcrumb } from './components/Breadcrumb'
|
export { Breadcrumb } from './components/Breadcrumb'
|
||||||
export type {
|
export type { BreadcrumbProps, BreadcrumbItem } from './components/Breadcrumb'
|
||||||
BreadcrumbProps,
|
|
||||||
BreadcrumbItem,
|
|
||||||
BreadcrumbLinkComponent,
|
|
||||||
} from './components/Breadcrumb'
|
|
||||||
export { Offcanvas } from './components/Offcanvas'
|
export { Offcanvas } from './components/Offcanvas'
|
||||||
export { Modal, ConfirmDialog } from './components/Modal'
|
export { Modal, ConfirmDialog } from './components/Modal'
|
||||||
export type { ModalProps, ModalSize, ConfirmDialogProps } from './components/Modal'
|
export type { ModalProps, ModalSize, ConfirmDialogProps } from './components/Modal'
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@maks-it.com/webui",
|
"name": "@maks-it.com/webui",
|
||||||
"version": "0.4.4",
|
"version": "0.4.5",
|
||||||
"description": "Shared contracts, utilities, and React components for MaksIT WebUI apps",
|
"description": "Shared contracts, utilities, and React components for MaksIT WebUI apps",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./dist/index.cjs",
|
"main": "./dist/index.cjs",
|
||||||
|
|||||||
@ -11,7 +11,7 @@ const meta = {
|
|||||||
docs: {
|
docs: {
|
||||||
description: {
|
description: {
|
||||||
component:
|
component:
|
||||||
'Presentational page trail (`nav` + `ol`). Never uses headings — keep a single page `h1` on `FormHeader` or the page title. Inject `linkComponent` (e.g. react-router `Link`) for SPA navigation.',
|
'Page trail (`nav` + `ol`) using react-router `Link`. Never uses headings — keep a single page `h1` on `FormHeader` or the page title.',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -47,15 +47,14 @@ export const SingleItem: Story = {
|
|||||||
export const WithFormHeader: Story = {
|
export const WithFormHeader: Story = {
|
||||||
render: () => (
|
render: () => (
|
||||||
<div className="space-y-0 border border-gray-200 bg-white">
|
<div className="space-y-0 border border-gray-200 bg-white">
|
||||||
<div className="bg-gray-50 px-4 py-2">
|
|
||||||
<Breadcrumb
|
<Breadcrumb
|
||||||
|
className="bg-gray-50 px-4 py-2"
|
||||||
items={[
|
items={[
|
||||||
{ label: 'Admin', to: '/admin' },
|
{ label: 'Admin', to: '/admin' },
|
||||||
{ label: 'Shop', to: '/admin/shop' },
|
{ label: 'Shop', to: '/admin/shop' },
|
||||||
{ label: 'Edit item' },
|
{ label: 'Edit item' },
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
<FormHeader>Edit shop item</FormHeader>
|
<FormHeader>Edit shop item</FormHeader>
|
||||||
<div className="p-4 text-sm text-gray-600">Form content</div>
|
<div className="p-4 text-sm text-gray-600">Form content</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user