Skip to content

Commit 971fb7b

Browse files
feat: new headless and primitive docs part IV
1 parent 3565182 commit 971fb7b

50 files changed

Lines changed: 2610 additions & 836 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
'use client';
2+
import { ChevronRight } from '@primeicons/react/chevron-right';
3+
import { Home } from '@primeicons/react/home';
4+
import { useBreadcrumb } from '@primereact/headless/breadcrumb';
5+
6+
export default function BasicDemo() {
7+
const { rootProps, separatorProps } = useBreadcrumb();
8+
9+
return (
10+
<div className="flex justify-center">
11+
<nav {...rootProps} className="bg-surface-0 dark:bg-surface-900 py-3 px-4 rounded-lg overflow-x-auto">
12+
<ol className="m-0 p-0 list-none flex items-center flex-nowrap gap-2">
13+
<li className="flex items-center gap-2">
14+
<a
15+
href="/"
16+
className="no-underline flex items-center gap-2 text-surface-500 dark:text-surface-400 hover:text-surface-700 dark:hover:text-surface-0 transition-colors rounded-md focus-visible:outline-1 focus-visible:outline-offset-2 focus-visible:outline-primary"
17+
>
18+
<Home className="w-4 h-4" />
19+
</a>
20+
</li>
21+
<li {...separatorProps} className="flex items-center text-surface-400 dark:text-surface-500">
22+
<ChevronRight className="w-3 h-3" />
23+
</li>
24+
<li className="flex items-center gap-2">
25+
<a
26+
href="#"
27+
className="no-underline text-sm text-surface-500 dark:text-surface-400 hover:text-surface-700 dark:hover:text-surface-0 transition-colors rounded-md focus-visible:outline-1 focus-visible:outline-offset-2 focus-visible:outline-primary"
28+
>
29+
Products
30+
</a>
31+
</li>
32+
<li {...separatorProps} className="flex items-center text-surface-400 dark:text-surface-500">
33+
<ChevronRight className="w-3 h-3" />
34+
</li>
35+
<li className="flex items-center gap-2">
36+
<a
37+
href="#"
38+
className="no-underline text-sm text-surface-500 dark:text-surface-400 hover:text-surface-700 dark:hover:text-surface-0 transition-colors rounded-md focus-visible:outline-1 focus-visible:outline-offset-2 focus-visible:outline-primary"
39+
>
40+
Electronics
41+
</a>
42+
</li>
43+
<li {...separatorProps} className="flex items-center text-surface-400 dark:text-surface-500">
44+
<ChevronRight className="w-3 h-3" />
45+
</li>
46+
<li className="flex items-center gap-2">
47+
<span className="text-sm text-surface-950 dark:text-surface-0" aria-current="page">
48+
Laptops
49+
</span>
50+
</li>
51+
</ol>
52+
</nav>
53+
</div>
54+
);
55+
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
'use client';
2+
import { Bars } from '@primeicons/react/bars';
3+
import { Times } from '@primeicons/react/times';
4+
import { useMotion } from '@primereact/core/motion';
5+
import { useDrawer } from '@primereact/headless/drawer';
6+
import { usePortal } from '@primereact/headless/portal';
7+
import * as React from 'react';
8+
import { createPortal } from 'react-dom';
9+
10+
const navItems = [
11+
{ icon: 'pi pi-objects-column', label: 'Dashboard' },
12+
{ icon: 'pi pi-inbox', label: 'Inbox' },
13+
{ icon: 'pi pi-calendar', label: 'Calendar' },
14+
{ icon: 'pi pi-chart-bar', label: 'Analytics' }
15+
];
16+
17+
export default function BasicDemo() {
18+
const { triggerProps, backdropProps, portalProps, closeProps, headerProps, rootProps, portalMotionProps, backdropMotionProps, setMaskRef } =
19+
useDrawer();
20+
const portal = usePortal();
21+
22+
const portalRef = React.useRef<HTMLDivElement>(null);
23+
const portalMotion = useMotion({
24+
...portalMotionProps.motionProps,
25+
elementRef: portalRef,
26+
visible: portalMotionProps.visible,
27+
enterFromClassName: '-translate-x-full',
28+
enterActiveClassName: 'transition-transform duration-200',
29+
enterToClassName: 'translate-x-0',
30+
leaveFromClassName: 'translate-x-0',
31+
leaveActiveClassName: 'transition-transform duration-200',
32+
leaveToClassName: '-translate-x-full'
33+
});
34+
35+
const backdropMotion = useMotion({
36+
...backdropMotionProps.motionProps,
37+
elementRef: setMaskRef,
38+
visible: backdropMotionProps.visible,
39+
enterFromClassName: 'opacity-0',
40+
enterActiveClassName: 'transition-opacity duration-200',
41+
enterToClassName: 'opacity-100',
42+
leaveFromClassName: 'opacity-100',
43+
leaveActiveClassName: 'transition-opacity duration-200',
44+
leaveToClassName: 'opacity-0'
45+
});
46+
47+
return (
48+
<div {...rootProps} className="flex justify-center">
49+
<button
50+
{...triggerProps}
51+
className="inline-flex items-center justify-center w-10 h-10 rounded-lg border border-surface bg-surface-0 dark:bg-surface-950 hover:bg-surface-100 dark:hover:bg-surface-900 cursor-pointer focus-visible:outline focus-visible:outline-1 focus-visible:outline-offset-2 focus-visible:outline-primary transition"
52+
>
53+
<Bars />
54+
</button>
55+
56+
{portal.state.mounted &&
57+
portalMotion.state.rendered &&
58+
createPortal(
59+
<div className="fixed inset-0 z-1100">
60+
{backdropMotion.state.rendered && <div ref={setMaskRef} {...backdropProps} className="fixed inset-0 bg-black/50" />}
61+
{portalMotion.state.rendered && (
62+
<div
63+
{...portalProps}
64+
ref={portalRef}
65+
className="fixed top-0 left-0 h-full w-80 bg-surface-0 dark:bg-surface-900 shadow-2xl flex flex-col z-50"
66+
>
67+
<div {...headerProps} className="flex items-center justify-between p-4 border-b border-surface">
68+
<span className="text-lg font-semibold">Navigation</span>
69+
<button
70+
{...closeProps}
71+
className="inline-flex items-center justify-center w-8 h-8 rounded-full cursor-pointer bg-surface-50 dark:bg-surface-900 hover:bg-surface-100 dark:hover:bg-surface-800 focus-visible:outline focus-visible:outline-1 focus-visible:outline-offset-2 focus-visible:outline-primary transition"
72+
>
73+
<Times />
74+
</button>
75+
</div>
76+
<nav className="flex-1 p-3">
77+
<ul className="list-none m-0 p-0">
78+
{navItems.map((item) => (
79+
<li key={item.label}>
80+
<a className="flex items-center gap-3 px-3 py-2.5 rounded-lg cursor-pointer hover:bg-surface-50 dark:hover:bg-surface-800 transition text-sm font-medium">
81+
<i className={`${item.icon} text-base`} />
82+
{item.label}
83+
</a>
84+
</li>
85+
))}
86+
</ul>
87+
</nav>
88+
</div>
89+
)}
90+
</div>,
91+
document.body
92+
)}
93+
</div>
94+
);
95+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
'use client';
2+
import { useMeterGroup } from '@primereact/headless/metergroup';
3+
4+
const values = [
5+
{ label: 'Apps', value: 16 },
6+
{ label: 'Messages', value: 8 },
7+
{ label: 'Media', value: 24 },
8+
{ label: 'System', value: 10 }
9+
];
10+
11+
export default function BasicDemo() {
12+
const metergroup = useMeterGroup();
13+
const { rootProps, percent, percentAsString } = metergroup;
14+
15+
return (
16+
<div className="flex flex-col gap-4 max-w-md mx-auto">
17+
<div {...rootProps} className="flex flex-col gap-3">
18+
<div className="flex h-2 rounded-full bg-surface-100 dark:bg-surface-800 overflow-hidden">
19+
{values.map((item, index) => {
20+
const colors = ['bg-primary', 'bg-emerald-500', 'bg-violet-500', 'bg-amber-500'];
21+
22+
return <div key={index} className={`${colors[index]} transition-all`} style={{ width: percentAsString(item.value) }} />;
23+
})}
24+
</div>
25+
<ol className="flex flex-wrap gap-x-4 gap-y-1 list-none p-0 m-0">
26+
{values.map((item, index) => {
27+
const colors = ['bg-primary', 'bg-emerald-500', 'bg-violet-500', 'bg-amber-500'];
28+
29+
return (
30+
<li key={index} className="flex items-center gap-1.5 text-sm text-surface-600 dark:text-surface-300">
31+
<span className={`inline-block w-2 h-2 rounded-sm ${colors[index]}`} />
32+
{item.label} ({percent(item.value)}%)
33+
</li>
34+
);
35+
})}
36+
</ol>
37+
</div>
38+
</div>
39+
);
40+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
'use client';
2+
import { useProgressBar } from '@primereact/headless/progressbar';
3+
4+
export default function BasicDemo() {
5+
const { rootProps, trackProps, state } = useProgressBar({ value: 50 });
6+
7+
return (
8+
<div className="flex flex-col gap-6 max-w-lg mx-auto">
9+
<div {...rootProps}>
10+
<div {...trackProps} className="h-5 rounded-full bg-surface-100 dark:bg-surface-800 overflow-hidden">
11+
<div
12+
className="flex items-center justify-center h-full bg-primary rounded-full transition-all duration-500"
13+
style={{ width: state.computedValue + '%' }}
14+
>
15+
<span className="flex items-center justify-center text-xs font-semibold text-surface-200">{state.formattedValue}</span>
16+
</div>
17+
</div>
18+
</div>
19+
</div>
20+
);
21+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
'use client';
2+
import { TerminalResponse, useTerminal } from '@primereact/headless/terminal';
3+
4+
export default function BasicDemo() {
5+
const commandHandler = (text: string): TerminalResponse => {
6+
const command = text.trim().split(/\s+/)[0];
7+
8+
switch (command) {
9+
case 'help':
10+
return 'Available commands: date, greet, random, clear';
11+
case 'date':
12+
return new Date().toLocaleString();
13+
case 'greet':
14+
return `Hello, ${text.substring(text.indexOf(' ') + 1).trim() || 'World'}!`;
15+
case 'random':
16+
return `Your random number: ${Math.floor(Math.random() * 100)}`;
17+
case 'clear':
18+
return null;
19+
default:
20+
return `Unknown command: ${command}. Type "help" for available commands.`;
21+
}
22+
};
23+
24+
const { rootProps, promptValueProps, state } = useTerminal({ prompt: '$', onCommand: commandHandler });
25+
26+
return (
27+
<div className="max-w-2xl mx-auto">
28+
<div {...rootProps} className="bg-surface-900 text-surface-0 rounded-lg p-4 h-72 overflow-y-auto cursor-text font-mono text-sm">
29+
<div className="text-surface-400 mb-2">Welcome to PrimeReact Terminal. Type &quot;help&quot; for available commands.</div>
30+
<div className="flex flex-col gap-1">
31+
{state.commands.map((cmd, index) => (
32+
<div key={index}>
33+
<div className="flex gap-2">
34+
<span className="text-emerald-400">$</span>
35+
<span>{cmd.text}</span>
36+
</div>
37+
{cmd.response && (
38+
<div className="text-surface-300 whitespace-pre-wrap pl-4" aria-live="polite">
39+
{cmd.response}
40+
</div>
41+
)}
42+
</div>
43+
))}
44+
</div>
45+
<div className="flex gap-2 items-center mt-1">
46+
<span className="text-emerald-400">$</span>
47+
<input {...promptValueProps} className="flex-1 bg-transparent border-none outline-none text-surface-0 p-0 font-mono text-sm" />
48+
</div>
49+
</div>
50+
</div>
51+
);
52+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
'use client';
2+
import { ChevronRight } from '@primeicons/react/chevron-right';
3+
import { usePositioner } from '@primereact/headless/positioner';
4+
import { useTooltip } from '@primereact/headless/tooltip';
5+
import { useTooltipManager } from '@primereact/headless/tooltipmanager';
6+
import * as React from 'react';
7+
import { createPortal } from 'react-dom';
8+
9+
const arrowStyles: Record<string, React.CSSProperties> = {
10+
top: { bottom: '-0.25rem', left: 'var(--placer-arrow-x)', transform: 'translateX(-50%) rotate(-45deg)' },
11+
bottom: { top: '-0.25rem', left: 'var(--placer-arrow-x)', transform: 'translateX(-50%) rotate(135deg)' },
12+
left: { right: '-0.25rem', top: 'var(--placer-arrow-y)', transform: 'translateY(-50%) rotate(-135deg)' },
13+
right: { left: '-0.25rem', top: 'var(--placer-arrow-y)', transform: 'translateY(-50%) rotate(45deg)' }
14+
};
15+
16+
const flights = [
17+
{ from: 'IST', to: 'LHR', airline: 'Turkish Airlines', status: 'ON TIME', color: 'bg-green-600 text-white' },
18+
{ from: 'IST', to: 'CDG', airline: 'Lufthansa', status: 'DELAYED', color: 'bg-primary text-primary-contrast' },
19+
{ from: 'IST', to: 'FRA', airline: 'Qatar Airways', status: 'BOARDING', color: 'bg-amber-600 text-white' }
20+
];
21+
22+
function FlightTooltip({ flight, manager }: { flight: (typeof flights)[0]; manager: ReturnType<typeof useTooltipManager> }) {
23+
const { triggerProps, popupProps, arrowProps, positionerProps, state, presence } = useTooltip({
24+
tooltipManager: manager,
25+
openDelay: 200,
26+
closeDelay: 100
27+
});
28+
29+
const positioner = usePositioner({
30+
anchor: state.anchorElement,
31+
content: state.positionerElement,
32+
arrow: state.arrowElement,
33+
side: 'top',
34+
sideOffset: 8,
35+
flip: true,
36+
shift: true
37+
});
38+
39+
const side = positioner.state.actualSide ?? 'top';
40+
41+
return (
42+
<>
43+
<button
44+
{...triggerProps}
45+
className="px-2.5 flex items-center justify-center cursor-pointer hover:bg-surface-50 dark:hover:bg-surface-800 transition-colors bg-transparent border-none text-sm text-surface-700 dark:text-surface-0"
46+
>
47+
{flight.from} <ChevronRight className="w-3 h-3 mx-1 opacity-50" /> {flight.to}
48+
</button>
49+
{presence.present &&
50+
createPortal(
51+
<div {...positionerProps}>
52+
<div
53+
{...popupProps}
54+
className="relative bg-surface-950 dark:bg-surface-0 text-surface-0 dark:text-surface-950 rounded-lg px-2.5 py-1.5 text-sm font-medium flex items-center gap-1 shadow-md transition-[opacity,scale] duration-200 origin-[var(--transform-origin)] opacity-0 scale-95 data-[open]:opacity-100 data-[open]:scale-100 data-[instant]:!duration-0"
55+
>
56+
<div
57+
{...arrowProps}
58+
className="absolute w-2 h-2 bg-surface-900 dark:bg-surface-50 [clip-path:polygon(0_100%,0_0,100%_100%)]"
59+
style={arrowStyles[side]}
60+
/>
61+
{flight.airline}{' '}
62+
<span
63+
className={`-mr-1 flex items-center justify-center leading-none px-1.5 py-1 rounded-sm text-xs font-medium ${flight.color}`}
64+
>
65+
{flight.status}
66+
</span>
67+
</div>
68+
</div>,
69+
document.body
70+
)}
71+
</>
72+
);
73+
}
74+
75+
export default function BasicDemo() {
76+
const manager = useTooltipManager();
77+
78+
return (
79+
<div className="flex justify-center">
80+
<div className="flex text-sm font-medium border border-surface-200 dark:border-surface-700 rounded-lg h-8 divide-x divide-surface-200 dark:divide-surface-700 [&>*:first-child]:rounded-l-lg [&>*:last-child]:rounded-r-lg overflow-hidden">
81+
{flights.map((flight) => (
82+
<FlightTooltip key={flight.to} flight={flight} manager={manager} />
83+
))}
84+
</div>
85+
</div>
86+
);
87+
}

apps/showcase/demo/primitives/badge/basic-demo.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@ export default function BasicDemo() {
1616
<div className={styles.row}>
1717
<div className={styles.iconWrapper}>
1818
<Bell className={styles.icon} />
19-
<Badge className={`${styles.overlay} ${styles.danger}`}>3</Badge>
19+
<Badge className={`${styles.overlay} ${styles.danger}`} />
2020
</div>
2121
<div className={styles.iconWrapper}>
2222
<Envelope className={styles.icon} />
23-
<Badge className={`${styles.overlay} ${styles.primary}`}>7</Badge>
23+
<Badge className={`${styles.overlay} ${styles.primary}`} />
2424
</div>
2525
<div className={styles.iconWrapper}>
2626
<Bell className={styles.icon} />

0 commit comments

Comments
 (0)