@djangocfg/ui-core 2.1.429 → 2.1.431
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/README.md +1 -1
- package/package.json +4 -4
- package/src/components/forms/popover-action-button/index.tsx +47 -0
- package/src/components/index.ts +4 -0
- package/src/components/navigation/popover-row-button/index.tsx +54 -0
- package/src/components/select/select.tsx +12 -2
- package/src/providers/UiProviders.tsx +39 -2
package/README.md
CHANGED
|
@@ -44,7 +44,7 @@ import { UiProviders, Button, Card } from '@djangocfg/ui-core';
|
|
|
44
44
|
</UiProviders>
|
|
45
45
|
```
|
|
46
46
|
|
|
47
|
-
`<UiProviders>` mounts Tooltip / Dialog / Toast
|
|
47
|
+
`<UiProviders>` mounts Tooltip / Dialog / Toast in the right order **and a top-level error `Boundary`** (a crash shows a recoverable fallback, not a white screen). **Mount it once at the root** — library components (and everything in `@djangocfg/ui-tools`) trust it to be there and never nest their own (a second `TooltipProvider` is the canonical "Tooltip must be used within TooltipProvider" trap). Pass `onError` to forward crashes to your logger, `errorFallback` for a custom (e.g. i18n) crash screen, or `errorBoundary={false}` to opt out.
|
|
48
48
|
|
|
49
49
|
## Catalogue
|
|
50
50
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@djangocfg/ui-core",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.431",
|
|
4
4
|
"description": "Pure React UI component library without Next.js dependencies - for Electron, Vite, CRA apps",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ui-components",
|
|
@@ -106,7 +106,7 @@
|
|
|
106
106
|
"check": "tsc --noEmit"
|
|
107
107
|
},
|
|
108
108
|
"peerDependencies": {
|
|
109
|
-
"@djangocfg/i18n": "^2.1.
|
|
109
|
+
"@djangocfg/i18n": "^2.1.431",
|
|
110
110
|
"consola": "^3.4.2",
|
|
111
111
|
"lucide-react": "^0.545.0",
|
|
112
112
|
"moment": "^2.30.1",
|
|
@@ -180,8 +180,8 @@
|
|
|
180
180
|
"@chenglou/pretext": "*"
|
|
181
181
|
},
|
|
182
182
|
"devDependencies": {
|
|
183
|
-
"@djangocfg/i18n": "^2.1.
|
|
184
|
-
"@djangocfg/typescript-config": "^2.1.
|
|
183
|
+
"@djangocfg/i18n": "^2.1.431",
|
|
184
|
+
"@djangocfg/typescript-config": "^2.1.431",
|
|
185
185
|
"@types/node": "^25.2.3",
|
|
186
186
|
"@types/react": "^19.2.15",
|
|
187
187
|
"@types/react-dom": "^19.2.3",
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
|
|
5
|
+
import { cn } from '../../../lib/utils';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* PopoverActionButton — the full-width icon+label list/footer row, macOS style:
|
|
9
|
+
*
|
|
10
|
+
* [ icon ] label…
|
|
11
|
+
*
|
|
12
|
+
* The canonical "jump to settings" / footer-link control inside popovers and
|
|
13
|
+
* menus (e.g. "Connection settings…", "Manage providers…"). Replaces the
|
|
14
|
+
* hand-rolled `<button className="flex w-full items-center gap-2 rounded px-2
|
|
15
|
+
* py-1.5 …">` that was copied across popover footers.
|
|
16
|
+
*
|
|
17
|
+
* Plain `<button>` underneath — extend it with any native button prop. The
|
|
18
|
+
* incoming `className` is merged last, so callers can still add layout tweaks
|
|
19
|
+
* (`mt-1`, etc.) without re-stating the base styling.
|
|
20
|
+
*/
|
|
21
|
+
export interface PopoverActionButtonProps
|
|
22
|
+
extends React.ComponentProps<"button"> {
|
|
23
|
+
/** Icon rendered before the label, in a fixed `h-3.5 w-3.5` slot. */
|
|
24
|
+
icon?: React.ReactNode;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const PopoverActionButton = React.forwardRef<HTMLButtonElement, PopoverActionButtonProps>(
|
|
28
|
+
({ className, icon, type = "button", children, ...props }, ref) => {
|
|
29
|
+
return (
|
|
30
|
+
<button
|
|
31
|
+
ref={ref}
|
|
32
|
+
type={type}
|
|
33
|
+
className={cn(
|
|
34
|
+
"flex w-full items-center gap-2 rounded px-2 py-1.5 text-left text-xs text-muted-foreground hover:bg-muted hover:text-foreground",
|
|
35
|
+
className,
|
|
36
|
+
)}
|
|
37
|
+
{...props}
|
|
38
|
+
>
|
|
39
|
+
{icon != null && <span className="h-3.5 w-3.5 shrink-0">{icon}</span>}
|
|
40
|
+
{children}
|
|
41
|
+
</button>
|
|
42
|
+
);
|
|
43
|
+
},
|
|
44
|
+
);
|
|
45
|
+
PopoverActionButton.displayName = "PopoverActionButton";
|
|
46
|
+
|
|
47
|
+
export { PopoverActionButton };
|
package/src/components/index.ts
CHANGED
|
@@ -27,6 +27,8 @@ export type { SmartOTPProps as OTPInputProps, OTPValidationMode, OTPPasteBehavio
|
|
|
27
27
|
export { ButtonGroup as ButtonGroupComponent } from './forms/button-group';
|
|
28
28
|
export { DownloadButton } from './forms/button-download';
|
|
29
29
|
export type { DownloadButtonProps } from './forms/button-download';
|
|
30
|
+
export { PopoverActionButton } from './forms/popover-action-button';
|
|
31
|
+
export type { PopoverActionButtonProps } from './forms/popover-action-button';
|
|
30
32
|
|
|
31
33
|
// Mask Input
|
|
32
34
|
export { MaskInput } from './forms/mask-input';
|
|
@@ -79,6 +81,8 @@ export { navigationMenuTriggerStyle, NavigationMenu, NavigationMenuList, Navigat
|
|
|
79
81
|
export { Menubar, MenubarMenu, MenubarTrigger, MenubarContent, MenubarItem, MenubarSeparator, MenubarLabel, MenubarCheckboxItem, MenubarRadioGroup, MenubarRadioItem, MenubarPortal, MenubarSubContent, MenubarSubTrigger, MenubarGroup, MenubarSub, MenubarShortcut } from './navigation/menubar';
|
|
80
82
|
export { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, DropdownMenuCheckboxItem, DropdownMenuRadioItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuGroup, DropdownMenuPortal, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuRadioGroup } from './navigation/dropdown-menu';
|
|
81
83
|
export { ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuItem, ContextMenuLabel, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger } from './navigation/context-menu';
|
|
84
|
+
export { PopoverRowButton } from './navigation/popover-row-button';
|
|
85
|
+
export type { PopoverRowButtonProps } from './navigation/popover-row-button';
|
|
82
86
|
export { MenuBuilder } from './navigation/menu';
|
|
83
87
|
export type { MenuItem, MenuActionItem, MenuSubmenuItem, MenuCheckboxItem, MenuRadioGroup, MenuRadioOption, MenuSeparator, MenuLabel, MenuSection, MenuCustom, MenuRowBase, MenuBuilderProps } from './navigation/menu';
|
|
84
88
|
export { Tabs, TabsContent, TabsList, TabsTrigger } from './navigation/tabs';
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { Check } from 'lucide-react';
|
|
4
|
+
import * as React from 'react';
|
|
5
|
+
|
|
6
|
+
import { cn } from '../../../lib/utils';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* PopoverRowButton — a selectable list-row button, macOS picker style:
|
|
10
|
+
*
|
|
11
|
+
* [ ✓ ] label…
|
|
12
|
+
*
|
|
13
|
+
* The canonical row for model / machine / provider pickers: full-width, left
|
|
14
|
+
* aligned, with a trailing-leading check that fades in on `selected`. Replaces
|
|
15
|
+
* the hand-rolled `<button className={cn("flex w-full items-center gap-2 px-3
|
|
16
|
+
* py-1.5 …", active && "bg-muted/60")}>` copied across pickers.
|
|
17
|
+
*
|
|
18
|
+
* Plain `<button>` underneath — extend it with any native button prop. The
|
|
19
|
+
* incoming `className` is merged last so callers can compose freely.
|
|
20
|
+
*/
|
|
21
|
+
export interface PopoverRowButtonProps
|
|
22
|
+
extends React.ComponentProps<"button"> {
|
|
23
|
+
/** Marks the row active — fills the background and shows the check. */
|
|
24
|
+
selected?: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const PopoverRowButton = React.forwardRef<HTMLButtonElement, PopoverRowButtonProps>(
|
|
28
|
+
({ className, selected = false, type = "button", children, ...props }, ref) => {
|
|
29
|
+
return (
|
|
30
|
+
<button
|
|
31
|
+
ref={ref}
|
|
32
|
+
type={type}
|
|
33
|
+
className={cn(
|
|
34
|
+
"flex w-full items-center gap-2 px-3 py-1.5 text-left text-xs",
|
|
35
|
+
"hover:bg-muted disabled:cursor-not-allowed disabled:opacity-40",
|
|
36
|
+
selected && "bg-muted/60",
|
|
37
|
+
className,
|
|
38
|
+
)}
|
|
39
|
+
{...props}
|
|
40
|
+
>
|
|
41
|
+
<Check
|
|
42
|
+
className={cn(
|
|
43
|
+
"h-3.5 w-3.5 shrink-0 text-primary",
|
|
44
|
+
selected ? "opacity-100" : "opacity-0",
|
|
45
|
+
)}
|
|
46
|
+
/>
|
|
47
|
+
{children}
|
|
48
|
+
</button>
|
|
49
|
+
);
|
|
50
|
+
},
|
|
51
|
+
);
|
|
52
|
+
PopoverRowButton.displayName = "PopoverRowButton";
|
|
53
|
+
|
|
54
|
+
export { PopoverRowButton };
|
|
@@ -53,12 +53,22 @@ const SelectTrigger = React.forwardRef<
|
|
|
53
53
|
React.ElementRef<typeof SelectPrimitive.Trigger>,
|
|
54
54
|
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger> & {
|
|
55
55
|
icon?: React.ComponentType<{ className?: string }>;
|
|
56
|
+
/**
|
|
57
|
+
* `ghost` strips the bordered field chrome (border, bg, shadow, fixed
|
|
58
|
+
* height) for use as a flat inline control — e.g. a compact dropdown in a
|
|
59
|
+
* status bar or toolbar where the trigger should read like a plain button.
|
|
60
|
+
*/
|
|
61
|
+
variant?: "default" | "ghost";
|
|
56
62
|
}
|
|
57
|
-
>(({ className, children, icon: Icon, ...props }, ref) => (
|
|
63
|
+
>(({ className, children, icon: Icon, variant = "default", ...props }, ref) => (
|
|
58
64
|
<SelectPrimitive.Trigger
|
|
59
65
|
ref={ref}
|
|
60
66
|
className={cn(
|
|
61
|
-
"flex
|
|
67
|
+
"flex items-center justify-between whitespace-nowrap text-sm ring-offset-background data-[placeholder]:text-muted-foreground focus:outline-none disabled:cursor-not-allowed disabled:opacity-50",
|
|
68
|
+
variant === "default" &&
|
|
69
|
+
"h-10 w-full rounded-[var(--radius)] border border-input bg-transparent px-3 py-2 shadow-sm focus:ring-1 focus:ring-ring",
|
|
70
|
+
variant === "ghost" &&
|
|
71
|
+
"rounded-md px-2 py-0.5 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground data-[state=open]:bg-muted data-[state=open]:text-foreground",
|
|
62
72
|
className
|
|
63
73
|
)}
|
|
64
74
|
{...props}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import * as React from 'react';
|
|
4
|
-
import {
|
|
4
|
+
import type { ErrorInfo } from 'react';
|
|
5
5
|
|
|
6
6
|
import { TooltipProvider } from '../components/overlay/tooltip';
|
|
7
7
|
import { Toaster } from '../components/feedback/sonner';
|
|
8
|
+
import { Boundary, type BoundaryProps } from '../components/boundary';
|
|
8
9
|
import { DialogProvider } from '../lib/dialog-service/DialogProvider';
|
|
9
10
|
|
|
10
11
|
export interface UiProvidersProps {
|
|
@@ -15,6 +16,23 @@ export interface UiProvidersProps {
|
|
|
15
16
|
noToaster?: boolean;
|
|
16
17
|
/** Disable the imperative `window.dialog` service + its renderer. */
|
|
17
18
|
noDialogService?: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Top-level crash protection. By default UiProviders wraps the whole tree in
|
|
21
|
+
* a fullscreen `<Boundary>` so any uncaught React error shows a recoverable
|
|
22
|
+
* fallback instead of a white screen. Pass `onError` to forward the crash to
|
|
23
|
+
* your logger / monitor (the host owns logging; the boundary just calls it).
|
|
24
|
+
* Set `errorBoundary={false}` if the host installs its own top-level boundary.
|
|
25
|
+
*/
|
|
26
|
+
onError?: (error: Error, info: ErrorInfo) => void;
|
|
27
|
+
/** Disable the built-in top-level error boundary. @default false (boundary on) */
|
|
28
|
+
errorBoundary?: false;
|
|
29
|
+
/**
|
|
30
|
+
* Custom fallback for the built-in boundary. Receives `{ error, errorInfo,
|
|
31
|
+
* reset }`. Use it to render an app-specific (e.g. i18n'd) crash screen
|
|
32
|
+
* instead of the default fullscreen one — lets hosts keep a single boundary
|
|
33
|
+
* (this one) rather than wrapping their own on top.
|
|
34
|
+
*/
|
|
35
|
+
errorFallback?: BoundaryProps['fallback'];
|
|
18
36
|
/**
|
|
19
37
|
* SSR-safe mount strategy. Default `true` — skips providers on the
|
|
20
38
|
* initial server render and remounts after `useEffect` so Radix
|
|
@@ -60,6 +78,9 @@ export function UiProviders({
|
|
|
60
78
|
tooltipDelay = 100,
|
|
61
79
|
noToaster,
|
|
62
80
|
noDialogService,
|
|
81
|
+
onError,
|
|
82
|
+
errorBoundary,
|
|
83
|
+
errorFallback,
|
|
63
84
|
}: UiProvidersProps) {
|
|
64
85
|
// No SSR-skip on purpose: any nested library component that renders
|
|
65
86
|
// `<Tooltip>` on its first paint expects to find `<TooltipProvider>`
|
|
@@ -67,7 +88,23 @@ export function UiProviders({
|
|
|
67
88
|
// "Tooltip must be used within TooltipProvider" before hydration.
|
|
68
89
|
// Radix's own provider tolerates the SSR pass — no hydration
|
|
69
90
|
// mismatches observed; the safe wrapper was over-engineering.
|
|
70
|
-
const
|
|
91
|
+
const dialogTree = noDialogService ? children : <DialogProvider>{children}</DialogProvider>;
|
|
92
|
+
// Top-level crash protection by default. The Boundary catches uncaught React
|
|
93
|
+
// errors and shows a recoverable fullscreen fallback; `onError` forwards the
|
|
94
|
+
// crash to the host's logger/monitor. Opt out with `errorBoundary={false}`.
|
|
95
|
+
const tree =
|
|
96
|
+
errorBoundary === false ? (
|
|
97
|
+
dialogTree
|
|
98
|
+
) : (
|
|
99
|
+
<Boundary
|
|
100
|
+
variant="fullscreen"
|
|
101
|
+
name="ui-providers"
|
|
102
|
+
onError={onError}
|
|
103
|
+
fallback={errorFallback}
|
|
104
|
+
>
|
|
105
|
+
{dialogTree}
|
|
106
|
+
</Boundary>
|
|
107
|
+
);
|
|
71
108
|
return (
|
|
72
109
|
<TooltipProvider delayDuration={tooltipDelay}>
|
|
73
110
|
{tree}
|