feat: add SegmentedTabs, custom DateTimePicker, redesign time range selector
All checks were successful
Build & Publish / publish (push) Successful in 44s
All checks were successful
Build & Publish / publish (push) Successful in 44s
New components: - SegmentedTabs — pill-style tabs with sliding animated indicator, trailing slot for custom content, MutationObserver for dynamic resizing - Custom DateTimePicker — replaces native datetime-local with calendar grid, hour/minute inputs, Now/Apply buttons, portal dropdown Time range selector redesign: - Uses SegmentedTabs with inline from/to DateTimePicker triggers - "now" shown as clickable placeholder when to-date is not explicitly set - Preset selection keeps to-date as "now" until user sets it - No more "Custom" button — last tab is the live date range Other improvements: - FilterPill gains activeColor prop for status-colored active states - TopBar and EventFeed status pills now use colored dots + activeColor - Inventory nav expanded to full component-level table of contents - COMPONENT_GUIDE.md updated with new components - DateRangePicker test updated for custom DateTimePicker Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -81,6 +81,21 @@
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.navSubLink {
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
color: var(--text-muted);
|
||||
text-decoration: none;
|
||||
padding: 2px 8px 2px 20px;
|
||||
border-radius: var(--radius-sm);
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.navSubLink:hover {
|
||||
background: var(--bg-hover);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.content {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
|
||||
@@ -4,10 +4,86 @@ import { PrimitivesSection } from './sections/PrimitivesSection'
|
||||
import { CompositesSection } from './sections/CompositesSection'
|
||||
import { LayoutSection } from './sections/LayoutSection'
|
||||
|
||||
const NAV_ITEMS = [
|
||||
{ label: 'Primitives', href: '#primitives' },
|
||||
{ label: 'Composites', href: '#composites' },
|
||||
{ label: 'Layout', href: '#layout' },
|
||||
const NAV_SECTIONS = [
|
||||
{
|
||||
label: 'Primitives',
|
||||
href: '#primitives',
|
||||
components: [
|
||||
{ label: 'Alert', href: '#alert' },
|
||||
{ label: 'Avatar', href: '#avatar' },
|
||||
{ label: 'Badge', href: '#badge' },
|
||||
{ label: 'Button', href: '#button' },
|
||||
{ label: 'Card', href: '#card' },
|
||||
{ label: 'Checkbox', href: '#checkbox' },
|
||||
{ label: 'CodeBlock', href: '#codeblock' },
|
||||
{ label: 'Collapsible', href: '#collapsible' },
|
||||
{ label: 'DateRangePicker', href: '#daterangepicker' },
|
||||
{ label: 'DateTimePicker', href: '#datetimepicker' },
|
||||
{ label: 'EmptyState', href: '#emptystate' },
|
||||
{ label: 'FilterPill', href: '#filterpill' },
|
||||
{ label: 'FormField', href: '#formfield' },
|
||||
{ label: 'InfoCallout', href: '#infocallout' },
|
||||
{ label: 'InlineEdit', href: '#inline-edit' },
|
||||
{ label: 'Input', href: '#input' },
|
||||
{ label: 'KeyboardHint', href: '#keyboardhint' },
|
||||
{ label: 'Label', href: '#label' },
|
||||
{ label: 'MonoText', href: '#monotext' },
|
||||
{ label: 'Pagination', href: '#pagination' },
|
||||
{ label: 'ProgressBar', href: '#progressbar' },
|
||||
{ label: 'Radio', href: '#radio' },
|
||||
{ label: 'SectionHeader', href: '#sectionheader' },
|
||||
{ label: 'Select', href: '#select' },
|
||||
{ label: 'Skeleton', href: '#skeleton' },
|
||||
{ label: 'Sparkline', href: '#sparkline' },
|
||||
{ label: 'Spinner', href: '#spinner' },
|
||||
{ label: 'StatCard', href: '#statcard' },
|
||||
{ label: 'StatusDot', href: '#statusdot' },
|
||||
{ label: 'Tag', href: '#tag' },
|
||||
{ label: 'Textarea', href: '#textarea' },
|
||||
{ label: 'Toggle', href: '#toggle' },
|
||||
{ label: 'Tooltip', href: '#tooltip' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Composites',
|
||||
href: '#composites',
|
||||
components: [
|
||||
{ label: 'Accordion', href: '#accordion' },
|
||||
{ label: 'AlertDialog', href: '#alertdialog' },
|
||||
{ label: 'AreaChart', href: '#areachart' },
|
||||
{ label: 'AvatarGroup', href: '#avatargroup' },
|
||||
{ label: 'BarChart', href: '#barchart' },
|
||||
{ label: 'Breadcrumb', href: '#breadcrumb' },
|
||||
{ label: 'CommandPalette', href: '#commandpalette' },
|
||||
{ label: 'ConfirmDialog', href: '#confirm-dialog' },
|
||||
{ label: 'DataTable', href: '#datatable' },
|
||||
{ label: 'DetailPanel', href: '#detailpanel' },
|
||||
{ label: 'Dropdown', href: '#dropdown' },
|
||||
{ label: 'EventFeed', href: '#eventfeed' },
|
||||
{ label: 'FilterBar', href: '#filterbar' },
|
||||
{ label: 'GroupCard', href: '#groupcard' },
|
||||
{ label: 'LineChart', href: '#linechart' },
|
||||
{ label: 'MenuItem', href: '#menuitem' },
|
||||
{ label: 'Modal', href: '#modal' },
|
||||
{ label: 'MultiSelect', href: '#multi-select' },
|
||||
{ label: 'Popover', href: '#popover' },
|
||||
{ label: 'ProcessorTimeline', href: '#processortimeline' },
|
||||
{ label: 'SegmentedTabs', href: '#segmented-tabs' },
|
||||
{ label: 'ShortcutsBar', href: '#shortcutsbar' },
|
||||
{ label: 'Tabs', href: '#tabs' },
|
||||
{ label: 'Toast', href: '#toast' },
|
||||
{ label: 'TreeView', href: '#treeview' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Layout',
|
||||
href: '#layout',
|
||||
components: [
|
||||
{ label: 'AppShell', href: '#appshell' },
|
||||
{ label: 'Sidebar', href: '#sidebar' },
|
||||
{ label: 'TopBar', href: '#topbar' },
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
export function Inventory() {
|
||||
@@ -20,14 +96,16 @@ export function Inventory() {
|
||||
|
||||
<div className={styles.body}>
|
||||
<nav className={styles.nav} aria-label="Component categories">
|
||||
<div className={styles.navSection}>
|
||||
<span className={styles.navLabel}>Categories</span>
|
||||
{NAV_ITEMS.map((item) => (
|
||||
<a key={item.href} href={item.href} className={styles.navLink}>
|
||||
{item.label}
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
{NAV_SECTIONS.map((section) => (
|
||||
<div key={section.href} className={styles.navSection}>
|
||||
<span className={styles.navLabel}>{section.label}</span>
|
||||
{section.components.map((component) => (
|
||||
<a key={component.href} href={component.href} className={styles.navSubLink}>
|
||||
{component.label}
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</nav>
|
||||
|
||||
<main className={styles.content}>
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
MultiSelect,
|
||||
Popover,
|
||||
ProcessorTimeline,
|
||||
SegmentedTabs,
|
||||
ShortcutsBar,
|
||||
Tabs,
|
||||
ToastProvider,
|
||||
@@ -227,6 +228,7 @@ export function CompositesSection() {
|
||||
{ label: 'Agents', value: 'agents', count: 6 },
|
||||
]
|
||||
const [activeTab, setActiveTab] = useState('overview')
|
||||
const [segTab, setSegTab] = useState('account')
|
||||
|
||||
// 21. TreeView
|
||||
const [selectedNode, setSelectedNode] = useState<string | undefined>('proc1')
|
||||
@@ -636,6 +638,28 @@ export function CompositesSection() {
|
||||
</div>
|
||||
</DemoCard>
|
||||
|
||||
{/* 19b. SegmentedTabs */}
|
||||
<DemoCard
|
||||
id="segmented-tabs"
|
||||
title="SegmentedTabs"
|
||||
description="Pill-style segmented tab bar with elevated active state. Same API as Tabs."
|
||||
>
|
||||
<div className={styles.demoAreaColumn} style={{ width: '100%' }}>
|
||||
<SegmentedTabs
|
||||
tabs={[
|
||||
{ label: 'Account', value: 'account' },
|
||||
{ label: 'Password', value: 'password' },
|
||||
{ label: 'Notifications', value: 'notifications', count: 3 },
|
||||
]}
|
||||
active={segTab}
|
||||
onChange={setSegTab}
|
||||
/>
|
||||
<div style={{ fontSize: 13, color: 'var(--text-muted)' }}>
|
||||
Active tab: <strong>{segTab}</strong>
|
||||
</div>
|
||||
</div>
|
||||
</DemoCard>
|
||||
|
||||
{/* 20. Toast */}
|
||||
<DemoCard
|
||||
id="toast"
|
||||
|
||||
Reference in New Issue
Block a user