@dashforge/tw 0.2.1-beta → 0.4.0-beta
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +257 -0
- package/CONSUMER-VALIDATION.md +130 -0
- package/PARITY.md +483 -0
- package/PERFORMANCE.md +169 -0
- package/dist/index.esm.js +801 -42
- package/dist/src/components/Accordion/Accordion.d.ts +21 -0
- package/dist/src/components/Accordion/Accordion.d.ts.map +1 -0
- package/dist/src/components/Accordion/accordion.types.d.ts +74 -0
- package/dist/src/components/Accordion/accordion.types.d.ts.map +1 -0
- package/dist/src/components/Accordion/accordion.variants.d.ts +58 -0
- package/dist/src/components/Accordion/accordion.variants.d.ts.map +1 -0
- package/dist/src/components/AppShell/AppShell.d.ts +14 -0
- package/dist/src/components/AppShell/AppShell.d.ts.map +1 -1
- package/dist/src/components/AppShell/appShell.variants.d.ts.map +1 -1
- package/dist/src/components/Autocomplete/Autocomplete.d.ts.map +1 -1
- package/dist/src/components/Autocomplete/autocomplete.variants.d.ts.map +1 -1
- package/dist/src/components/Breadcrumbs/breadcrumbs.variants.d.ts.map +1 -1
- package/dist/src/components/Checkbox/Checkbox.d.ts.map +1 -1
- package/dist/src/components/Dialog/Dialog.d.ts +17 -0
- package/dist/src/components/Dialog/Dialog.d.ts.map +1 -0
- package/dist/src/components/Dialog/dialog.types.d.ts +75 -0
- package/dist/src/components/Dialog/dialog.types.d.ts.map +1 -0
- package/dist/src/components/Dialog/dialog.variants.d.ts +80 -0
- package/dist/src/components/Dialog/dialog.variants.d.ts.map +1 -0
- package/dist/src/components/LeftNav/leftNav.variants.d.ts.map +1 -1
- package/dist/src/components/Popover/Popover.d.ts +15 -0
- package/dist/src/components/Popover/Popover.d.ts.map +1 -0
- package/dist/src/components/Popover/popover.types.d.ts +45 -0
- package/dist/src/components/Popover/popover.types.d.ts.map +1 -0
- package/dist/src/components/Popover/popover.variants.d.ts +34 -0
- package/dist/src/components/Popover/popover.variants.d.ts.map +1 -0
- package/dist/src/components/Snackbar/snackbar.variants.d.ts.map +1 -1
- package/dist/src/components/Switch/switch.variants.d.ts.map +1 -1
- package/dist/src/components/Tabs/Tabs.d.ts +15 -0
- package/dist/src/components/Tabs/Tabs.d.ts.map +1 -0
- package/dist/src/components/Tabs/tabs.types.d.ts +55 -0
- package/dist/src/components/Tabs/tabs.types.d.ts.map +1 -0
- package/dist/src/components/Tabs/tabs.variants.d.ts +82 -0
- package/dist/src/components/Tabs/tabs.variants.d.ts.map +1 -0
- package/dist/src/components/TextField/TextField.d.ts.map +1 -1
- package/dist/src/components/TextField/textField.types.d.ts +29 -3
- package/dist/src/components/TextField/textField.types.d.ts.map +1 -1
- package/dist/src/components/TextField/textField.variants.d.ts +6 -0
- package/dist/src/components/TextField/textField.variants.d.ts.map +1 -1
- package/dist/src/components/Tooltip/Tooltip.d.ts +29 -0
- package/dist/src/components/Tooltip/Tooltip.d.ts.map +1 -0
- package/dist/src/components/Tooltip/tooltip.types.d.ts +47 -0
- package/dist/src/components/Tooltip/tooltip.types.d.ts.map +1 -0
- package/dist/src/components/Tooltip/tooltip.variants.d.ts +34 -0
- package/dist/src/components/Tooltip/tooltip.variants.d.ts.map +1 -0
- package/dist/src/index.d.ts +16 -1
- package/dist/src/index.d.ts.map +1 -1
- package/package.json +6 -1
- package/src/components/Accordion/Accordion.test.tsx +95 -0
- package/src/components/Accordion/Accordion.tsx +97 -0
- package/src/components/Accordion/accordion.types.ts +66 -0
- package/src/components/Accordion/accordion.variants.ts +30 -0
- package/src/components/AppShell/AppShell.tsx +126 -1
- package/src/components/AppShell/appShell.variants.ts +8 -2
- package/src/components/Autocomplete/Autocomplete.tsx +77 -4
- package/src/components/Autocomplete/autocomplete.variants.ts +6 -0
- package/src/components/Breadcrumbs/breadcrumbs.variants.ts +5 -0
- package/src/components/Checkbox/Checkbox.tsx +43 -9
- package/src/components/Dialog/Dialog.test.tsx +96 -0
- package/src/components/Dialog/Dialog.tsx +93 -0
- package/src/components/Dialog/dialog.types.ts +62 -0
- package/src/components/Dialog/dialog.variants.ts +62 -0
- package/src/components/LeftNav/leftNav.variants.ts +12 -1
- package/src/components/Popover/Popover.test.tsx +72 -0
- package/src/components/Popover/Popover.tsx +55 -0
- package/src/components/Popover/popover.types.ts +42 -0
- package/src/components/Popover/popover.variants.ts +18 -0
- package/src/components/Snackbar/snackbar.variants.ts +4 -2
- package/src/components/Switch/switch.variants.ts +6 -1
- package/src/components/Tabs/Tabs.test.tsx +74 -0
- package/src/components/Tabs/Tabs.tsx +66 -0
- package/src/components/Tabs/tabs.types.ts +49 -0
- package/src/components/Tabs/tabs.variants.ts +58 -0
- package/src/components/TextField/TextField.tsx +29 -0
- package/src/components/TextField/textField.types.ts +23 -3
- package/src/components/TextField/textField.variants.ts +18 -0
- package/src/components/Tooltip/Tooltip.test.tsx +73 -0
- package/src/components/Tooltip/Tooltip.tsx +70 -0
- package/src/components/Tooltip/tooltip.types.ts +44 -0
- package/src/components/Tooltip/tooltip.variants.ts +18 -0
- package/src/index.ts +40 -1
|
@@ -6,9 +6,27 @@ import type { TextFieldVariants } from './textField.variants.js';
|
|
|
6
6
|
/**
|
|
7
7
|
* Per-slot overrides for `<TextField>`.
|
|
8
8
|
*
|
|
9
|
-
* Each slot accepts `{ className: string }`
|
|
10
|
-
*
|
|
11
|
-
*
|
|
9
|
+
* Each slot accepts `{ className: string }` (and, for `prefix` /
|
|
10
|
+
* `suffix`, a `children` node) so the override path is extensible —
|
|
11
|
+
* extra fields like `style`, `aria-*`, `data-*` can be added without
|
|
12
|
+
* a breaking change. Mirrors the MUI-side `slotProps` shape.
|
|
13
|
+
*
|
|
14
|
+
* **`prefix` / `suffix`** are inline adornments rendered INSIDE the
|
|
15
|
+
* `inputWrapper`, before / after the `<input>` element. Typical use:
|
|
16
|
+
* currency symbols, units, status icons. Passing only `className`
|
|
17
|
+
* (no `children`) is allowed but renders an empty slot — usually
|
|
18
|
+
* you'll always pair the two.
|
|
19
|
+
*
|
|
20
|
+
* ```tsx
|
|
21
|
+
* <TextField
|
|
22
|
+
* name="price"
|
|
23
|
+
* type="number"
|
|
24
|
+
* slotProps={{
|
|
25
|
+
* prefix: { children: '$' },
|
|
26
|
+
* suffix: { children: 'USD' },
|
|
27
|
+
* }}
|
|
28
|
+
* />
|
|
29
|
+
* ```
|
|
12
30
|
*/
|
|
13
31
|
export interface TextFieldSlotProps {
|
|
14
32
|
root?: { className?: string };
|
|
@@ -18,6 +36,8 @@ export interface TextFieldSlotProps {
|
|
|
18
36
|
input?: { className?: string };
|
|
19
37
|
helperText?: { className?: string };
|
|
20
38
|
errorText?: { className?: string };
|
|
39
|
+
prefix?: { children?: ReactNode; className?: string };
|
|
40
|
+
suffix?: { children?: ReactNode; className?: string };
|
|
21
41
|
}
|
|
22
42
|
|
|
23
43
|
/**
|
|
@@ -34,6 +34,24 @@ export const textFieldVariants = tv({
|
|
|
34
34
|
],
|
|
35
35
|
helperText: 'mt-1 text-sm text-neutral-600',
|
|
36
36
|
errorText: 'mt-1 text-sm text-danger-600',
|
|
37
|
+
/*
|
|
38
|
+
* `prefix` / `suffix` slot — inline adornment rendered before /
|
|
39
|
+
* after the input INSIDE the inputWrapper. `shrink-0` keeps the
|
|
40
|
+
* adornment width fixed so it doesn't compete for space with the
|
|
41
|
+
* input; `select-none` + `pointer-events-none` avoids
|
|
42
|
+
* accidentally stealing focus from the input on click (the
|
|
43
|
+
* inputWrapper handles focus via `focus-within`).
|
|
44
|
+
*/
|
|
45
|
+
prefix: [
|
|
46
|
+
'shrink-0 inline-flex items-center select-none pointer-events-none',
|
|
47
|
+
'text-neutral-500',
|
|
48
|
+
'mr-2',
|
|
49
|
+
],
|
|
50
|
+
suffix: [
|
|
51
|
+
'shrink-0 inline-flex items-center select-none pointer-events-none',
|
|
52
|
+
'text-neutral-500',
|
|
53
|
+
'ml-2',
|
|
54
|
+
],
|
|
37
55
|
},
|
|
38
56
|
variants: {
|
|
39
57
|
size: {
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { describe, it, expect, afterEach, beforeAll } from 'vitest';
|
|
4
|
+
import { render, screen, cleanup } from '@testing-library/react';
|
|
5
|
+
import { Tooltip } from './Tooltip.js';
|
|
6
|
+
|
|
7
|
+
void React;
|
|
8
|
+
afterEach(() => cleanup());
|
|
9
|
+
|
|
10
|
+
// jsdom doesn't implement ResizeObserver — Radix Floating UI requires it.
|
|
11
|
+
// Shim with a noop before any test mounts a Tooltip.
|
|
12
|
+
beforeAll(() => {
|
|
13
|
+
if (typeof globalThis.ResizeObserver === 'undefined') {
|
|
14
|
+
(globalThis as unknown as { ResizeObserver: unknown }).ResizeObserver = class {
|
|
15
|
+
observe() {}
|
|
16
|
+
unobserve() {}
|
|
17
|
+
disconnect() {}
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
if (typeof globalThis.DOMRect === 'undefined') {
|
|
21
|
+
(globalThis as unknown as { DOMRect: unknown }).DOMRect = class {
|
|
22
|
+
static fromRect(r?: DOMRectInit) {
|
|
23
|
+
return new (globalThis as unknown as { DOMRect: new (x: number, y: number, w: number, h: number) => unknown }).DOMRect(
|
|
24
|
+
r?.x ?? 0, r?.y ?? 0, r?.width ?? 0, r?.height ?? 0
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
constructor(public x = 0, public y = 0, public width = 0, public height = 0) {}
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
describe('Tooltip — rendering', () => {
|
|
33
|
+
it('renders the trigger (children) always', () => {
|
|
34
|
+
render(
|
|
35
|
+
<Tooltip content="Tip text">
|
|
36
|
+
<button>Trigger</button>
|
|
37
|
+
</Tooltip>
|
|
38
|
+
);
|
|
39
|
+
expect(screen.queryByRole('button', { name: 'Trigger' })).not.toBeNull();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('renders content when controlled open=true', () => {
|
|
43
|
+
render(
|
|
44
|
+
<Tooltip content="Tip text" open onOpenChange={() => {}}>
|
|
45
|
+
<button>Trigger</button>
|
|
46
|
+
</Tooltip>
|
|
47
|
+
);
|
|
48
|
+
expect(screen.queryAllByText('Tip text').length).toBeGreaterThan(0);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('does not render content when controlled open=false', () => {
|
|
52
|
+
render(
|
|
53
|
+
<Tooltip content="Tip text" open={false} onOpenChange={() => {}}>
|
|
54
|
+
<button>Trigger</button>
|
|
55
|
+
</Tooltip>
|
|
56
|
+
);
|
|
57
|
+
expect(screen.queryByText('Tip text')).toBeNull();
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
describe('Tooltip — placement props', () => {
|
|
62
|
+
it.each(['top', 'right', 'bottom', 'left'] as const)(
|
|
63
|
+
'renders side=%s without crashing',
|
|
64
|
+
(side) => {
|
|
65
|
+
render(
|
|
66
|
+
<Tooltip content="Tip" open side={side} onOpenChange={() => {}}>
|
|
67
|
+
<button>Trigger</button>
|
|
68
|
+
</Tooltip>
|
|
69
|
+
);
|
|
70
|
+
expect(screen.queryByRole('button', { name: 'Trigger' })).not.toBeNull();
|
|
71
|
+
}
|
|
72
|
+
);
|
|
73
|
+
});
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import * as RadixTooltip from '@radix-ui/react-tooltip';
|
|
2
|
+
import { cn } from '../../utils/cn.js';
|
|
3
|
+
import { tooltipVariants } from './tooltip.variants.js';
|
|
4
|
+
import type { TooltipProps } from './tooltip.types.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Dashforge TW `<Tooltip>` — hoverable / focusable tooltip.
|
|
8
|
+
*
|
|
9
|
+
* Built on `@radix-ui/react-tooltip`:
|
|
10
|
+
* - APG tooltip pattern: `role="tooltip"`, `aria-describedby` wired
|
|
11
|
+
* to the trigger automatically (Radix manages the id linkage).
|
|
12
|
+
* - Shows on hover OR keyboard focus.
|
|
13
|
+
* - Dismissed on Escape or pointer leave.
|
|
14
|
+
*
|
|
15
|
+
* Wrap any single React node — it becomes the trigger via Radix's
|
|
16
|
+
* `asChild` slot. The trigger inherits `aria-describedby` so screen
|
|
17
|
+
* readers announce the tooltip content along with the trigger.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```tsx
|
|
21
|
+
* <Tooltip content="Delete this item">
|
|
22
|
+
* <Button variant="ghost">
|
|
23
|
+
* <TrashIcon />
|
|
24
|
+
* </Button>
|
|
25
|
+
* </Tooltip>
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* Each consumer can also wrap the entire app once in
|
|
29
|
+
* `<RadixTooltip.Provider>` to share delay/skip configuration; we
|
|
30
|
+
* include a per-component provider here for ease-of-use.
|
|
31
|
+
*/
|
|
32
|
+
export function Tooltip(props: TooltipProps) {
|
|
33
|
+
const {
|
|
34
|
+
children,
|
|
35
|
+
content,
|
|
36
|
+
side = 'top',
|
|
37
|
+
align = 'center',
|
|
38
|
+
hideArrow = false,
|
|
39
|
+
delayDuration = 200,
|
|
40
|
+
open,
|
|
41
|
+
onOpenChange,
|
|
42
|
+
sideOffset = 4,
|
|
43
|
+
slotProps,
|
|
44
|
+
} = props;
|
|
45
|
+
|
|
46
|
+
const v = tooltipVariants();
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<RadixTooltip.Provider delayDuration={delayDuration}>
|
|
50
|
+
<RadixTooltip.Root open={open} onOpenChange={onOpenChange}>
|
|
51
|
+
<RadixTooltip.Trigger asChild>{children}</RadixTooltip.Trigger>
|
|
52
|
+
<RadixTooltip.Portal>
|
|
53
|
+
<RadixTooltip.Content
|
|
54
|
+
side={side}
|
|
55
|
+
align={align}
|
|
56
|
+
sideOffset={sideOffset}
|
|
57
|
+
className={cn(v.content(), slotProps?.content?.className)}
|
|
58
|
+
>
|
|
59
|
+
{content}
|
|
60
|
+
{!hideArrow && (
|
|
61
|
+
<RadixTooltip.Arrow
|
|
62
|
+
className={cn(v.arrow(), slotProps?.arrow?.className)}
|
|
63
|
+
/>
|
|
64
|
+
)}
|
|
65
|
+
</RadixTooltip.Content>
|
|
66
|
+
</RadixTooltip.Portal>
|
|
67
|
+
</RadixTooltip.Root>
|
|
68
|
+
</RadixTooltip.Provider>
|
|
69
|
+
);
|
|
70
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
|
|
3
|
+
export interface TooltipSlotProps {
|
|
4
|
+
content?: { className?: string };
|
|
5
|
+
arrow?: { className?: string };
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Props for `<Tooltip>`.
|
|
10
|
+
*
|
|
11
|
+
* Built on `@radix-ui/react-tooltip`. Wrap any element with a tooltip
|
|
12
|
+
* that follows APG semantics (`role="tooltip"`, `aria-describedby`
|
|
13
|
+
* wired to the trigger).
|
|
14
|
+
*
|
|
15
|
+
* Renders an arrow by default. Three side options
|
|
16
|
+
* (top / right / bottom / left) with 'top' as default. Delay
|
|
17
|
+
* configurable via `delayDuration` (forwarded to Radix Provider).
|
|
18
|
+
*/
|
|
19
|
+
export interface TooltipProps {
|
|
20
|
+
/** The element that, when hovered/focused, shows the tooltip. */
|
|
21
|
+
children: ReactNode;
|
|
22
|
+
/** Tooltip body content. */
|
|
23
|
+
content: ReactNode;
|
|
24
|
+
/** Side of the trigger to show the tooltip on. @default 'top' */
|
|
25
|
+
side?: 'top' | 'right' | 'bottom' | 'left';
|
|
26
|
+
/** Alignment along the perpendicular axis. @default 'center' */
|
|
27
|
+
align?: 'start' | 'center' | 'end';
|
|
28
|
+
/** Hide the arrow indicator. @default false (arrow shown) */
|
|
29
|
+
hideArrow?: boolean;
|
|
30
|
+
/** Delay before the tooltip shows on hover (ms). @default 200 */
|
|
31
|
+
delayDuration?: number;
|
|
32
|
+
/**
|
|
33
|
+
* Controlled open state. Use when the tooltip should be programmatic
|
|
34
|
+
* (e.g. revealed on a manual button press). Omit for default
|
|
35
|
+
* hover / focus behavior.
|
|
36
|
+
*/
|
|
37
|
+
open?: boolean;
|
|
38
|
+
/** Called when the tooltip's open state changes. */
|
|
39
|
+
onOpenChange?: (open: boolean) => void;
|
|
40
|
+
/** Side offset in pixels from the trigger. @default 4 */
|
|
41
|
+
sideOffset?: number;
|
|
42
|
+
/** Per-slot overrides. */
|
|
43
|
+
slotProps?: TooltipSlotProps;
|
|
44
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { tv, type VariantProps } from 'tailwind-variants';
|
|
2
|
+
|
|
3
|
+
export const tooltipVariants = tv({
|
|
4
|
+
slots: {
|
|
5
|
+
content: [
|
|
6
|
+
'z-50 max-w-xs rounded-md px-3 py-1.5 text-xs font-medium',
|
|
7
|
+
'bg-neutral-900 text-neutral-50',
|
|
8
|
+
'dark:bg-neutral-50 dark:text-neutral-900',
|
|
9
|
+
'shadow-md',
|
|
10
|
+
'data-[state=delayed-open]:animate-in data-[state=delayed-open]:fade-in-0 data-[state=delayed-open]:zoom-in-95',
|
|
11
|
+
'data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95',
|
|
12
|
+
'motion-reduce:transition-none motion-reduce:duration-0',
|
|
13
|
+
],
|
|
14
|
+
arrow: 'fill-neutral-900 dark:fill-neutral-50',
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
export type TooltipVariants = VariantProps<typeof tooltipVariants>;
|
package/src/index.ts
CHANGED
|
@@ -223,6 +223,45 @@ export type { AspectRatioProps } from './components/AspectRatio/aspectRatio.type
|
|
|
223
223
|
export { VisuallyHidden } from './components/VisuallyHidden/VisuallyHidden.js';
|
|
224
224
|
export type { VisuallyHiddenProps } from './components/VisuallyHidden/visuallyHidden.types.js';
|
|
225
225
|
|
|
226
|
+
// F11 tier-4: overlay + disclosure primitives.
|
|
227
|
+
|
|
228
|
+
export { Dialog } from './components/Dialog/Dialog.js';
|
|
229
|
+
export type {
|
|
230
|
+
DialogProps,
|
|
231
|
+
DialogSlotProps,
|
|
232
|
+
} from './components/Dialog/dialog.types.js';
|
|
233
|
+
export { dialogVariants } from './components/Dialog/dialog.variants.js';
|
|
234
|
+
|
|
235
|
+
export { Tabs } from './components/Tabs/Tabs.js';
|
|
236
|
+
export type {
|
|
237
|
+
TabsProps,
|
|
238
|
+
TabsSlotProps,
|
|
239
|
+
TabItem,
|
|
240
|
+
} from './components/Tabs/tabs.types.js';
|
|
241
|
+
export { tabsVariants } from './components/Tabs/tabs.variants.js';
|
|
242
|
+
|
|
243
|
+
export { Tooltip } from './components/Tooltip/Tooltip.js';
|
|
244
|
+
export type {
|
|
245
|
+
TooltipProps,
|
|
246
|
+
TooltipSlotProps,
|
|
247
|
+
} from './components/Tooltip/tooltip.types.js';
|
|
248
|
+
export { tooltipVariants } from './components/Tooltip/tooltip.variants.js';
|
|
249
|
+
|
|
250
|
+
export { Popover } from './components/Popover/Popover.js';
|
|
251
|
+
export type {
|
|
252
|
+
PopoverProps,
|
|
253
|
+
PopoverSlotProps,
|
|
254
|
+
} from './components/Popover/popover.types.js';
|
|
255
|
+
export { popoverVariants } from './components/Popover/popover.variants.js';
|
|
256
|
+
|
|
257
|
+
export { Accordion } from './components/Accordion/Accordion.js';
|
|
258
|
+
export type {
|
|
259
|
+
AccordionProps,
|
|
260
|
+
AccordionSlotProps,
|
|
261
|
+
AccordionItem,
|
|
262
|
+
} from './components/Accordion/accordion.types.js';
|
|
263
|
+
export { accordionVariants } from './components/Accordion/accordion.variants.js';
|
|
264
|
+
|
|
226
265
|
// ───── Hooks ─────
|
|
227
266
|
export { useAccessState } from './hooks/useAccessState.js';
|
|
228
267
|
|
|
@@ -237,4 +276,4 @@ export type { VariantProps } from 'tailwind-variants';
|
|
|
237
276
|
/**
|
|
238
277
|
* Package version (synced with `package.json` at publish time).
|
|
239
278
|
*/
|
|
240
|
-
export const VERSION = '0.
|
|
279
|
+
export const VERSION = '0.4.0-beta';
|