@djangocfg/ui-core 2.1.36 → 2.1.38

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@djangocfg/ui-core",
3
- "version": "2.1.36",
3
+ "version": "2.1.38",
4
4
  "description": "Pure React UI component library without Next.js dependencies - for Electron, Vite, CRA apps",
5
5
  "keywords": [
6
6
  "ui-components",
@@ -102,7 +102,7 @@
102
102
  "vaul": "1.1.2"
103
103
  },
104
104
  "devDependencies": {
105
- "@djangocfg/typescript-config": "^2.1.36",
105
+ "@djangocfg/typescript-config": "^2.1.38",
106
106
  "@types/node": "^24.7.2",
107
107
  "@types/react": "^19.1.0",
108
108
  "@types/react-dom": "^19.1.0",
@@ -46,6 +46,21 @@ export interface ButtonProps
46
46
  loading?: boolean
47
47
  }
48
48
 
49
+ // Filter out SVG icons from children when loading
50
+ function filterIcons(children: React.ReactNode): React.ReactNode {
51
+ return React.Children.map(children, (child) => {
52
+ if (!React.isValidElement(child)) return child;
53
+
54
+ const type = child.type;
55
+ // Skip native svg or ForwardRef components (Lucide icons)
56
+ if (type === 'svg' || (typeof type === 'object' && type !== null)) {
57
+ return null;
58
+ }
59
+
60
+ return child;
61
+ });
62
+ }
63
+
49
64
  const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
50
65
  ({ className, variant, size, asChild = false, loading = false, onClick, children, disabled, ...props }, ref) => {
51
66
  const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
@@ -77,8 +92,8 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
77
92
  disabled={disabled || loading}
78
93
  {...props}
79
94
  >
80
- {loading && <Loader2 className="animate-spin" />}
81
- {children}
95
+ {loading && <Loader2 className="size-4 animate-spin" />}
96
+ {loading ? filterIcons(children) : children}
82
97
  </button>
83
98
  )
84
99
  }
@@ -1,5 +1,37 @@
1
1
  "use client"
2
2
 
3
+ /*
4
+ * Sticky Component - react-sticky-box wrapper
5
+ *
6
+ * COMMON ISSUES & SOLUTIONS:
7
+ *
8
+ * 1. STICKY NOT WORKING
9
+ * Required structure:
10
+ * - Outer div: h-full overflow-y-auto (scrollable container)
11
+ * - Inner div: flex items-start min-h-full (flex container)
12
+ * - Main content: flex-1
13
+ * - Sticky: direct child of flex container
14
+ *
15
+ * 2. PAGE DOESN'T SCROLL
16
+ * Add "h-full overflow-y-auto" to outer container.
17
+ * Scrollable container must have defined height.
18
+ *
19
+ * 3. INSIDE ResizablePanel
20
+ * ResizablePanel has overflow-hidden. Add scrollable wrapper inside:
21
+ * <ResizablePanel>
22
+ * <div className="h-full overflow-y-auto">...</div>
23
+ * </ResizablePanel>
24
+ *
25
+ * 4. FLEX CONTAINER REQUIREMENTS
26
+ * - Use items-start (NOT items-stretch)
27
+ * - Sticky must be direct child of flex container
28
+ * - Main content should be flex-1
29
+ *
30
+ * 5. useNativeSticky vs react-sticky-box
31
+ * - useNativeSticky: CSS position:sticky, simpler but limited
32
+ * - Default: react-sticky-box, better for sidebars, handles tall content
33
+ */
34
+
3
35
  import * as React from 'react';
4
36
  import StickyBox from 'react-sticky-box';
5
37
 
@@ -32,6 +32,12 @@ const toastVariants = cva(
32
32
  default: "border bg-background text-foreground",
33
33
  destructive:
34
34
  "destructive group border-destructive bg-destructive text-destructive-foreground",
35
+ success:
36
+ "success group border-green-600 bg-green-500 text-white",
37
+ warning:
38
+ "warning group border-yellow-600 bg-yellow-500 text-black",
39
+ info:
40
+ "info group border-blue-600 bg-blue-500 text-white",
35
41
  },
36
42
  },
37
43
  defaultVariants: {
@@ -62,7 +68,11 @@ const ToastAction = React.forwardRef<
62
68
  <ToastPrimitives.Action
63
69
  ref={ref}
64
70
  className={cn(
65
- "inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium ring-offset-background transition-colors hover:bg-secondary focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 group-[.destructive]:border-muted/40 group-[.destructive]:hover:border-destructive/30 group-[.destructive]:hover:bg-destructive group-[.destructive]:hover:text-destructive-foreground group-[.destructive]:focus:ring-destructive",
71
+ "inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium ring-offset-background transition-colors hover:bg-secondary focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
72
+ "group-[.destructive]:border-muted/40 group-[.destructive]:hover:border-destructive/30 group-[.destructive]:hover:bg-destructive group-[.destructive]:hover:text-destructive-foreground group-[.destructive]:focus:ring-destructive",
73
+ "group-[.success]:border-green-300/40 group-[.success]:hover:bg-green-600 group-[.success]:hover:text-white group-[.success]:focus:ring-green-400",
74
+ "group-[.warning]:border-yellow-600/40 group-[.warning]:hover:bg-yellow-600 group-[.warning]:hover:text-black group-[.warning]:focus:ring-yellow-400",
75
+ "group-[.info]:border-blue-300/40 group-[.info]:hover:bg-blue-600 group-[.info]:hover:text-white group-[.info]:focus:ring-blue-400",
66
76
  className
67
77
  )}
68
78
  {...props}
@@ -77,7 +87,11 @@ const ToastClose = React.forwardRef<
77
87
  <ToastPrimitives.Close
78
88
  ref={ref}
79
89
  className={cn(
80
- "absolute right-2 top-2 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-2 group-hover:opacity-100 group-[.destructive]:text-red-300 group-[.destructive]:hover:text-red-50 group-[.destructive]:focus:ring-red-400 group-[.destructive]:focus:ring-offset-red-600",
90
+ "absolute right-2 top-2 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-2 group-hover:opacity-100",
91
+ "group-[.destructive]:text-red-300 group-[.destructive]:hover:text-red-50 group-[.destructive]:focus:ring-red-400 group-[.destructive]:focus:ring-offset-red-600",
92
+ "group-[.success]:text-green-100 group-[.success]:hover:text-white group-[.success]:focus:ring-green-300",
93
+ "group-[.warning]:text-yellow-800 group-[.warning]:hover:text-black group-[.warning]:focus:ring-yellow-600",
94
+ "group-[.info]:text-blue-100 group-[.info]:hover:text-white group-[.info]:focus:ring-blue-300",
81
95
  className
82
96
  )}
83
97
  toast-close=""
@@ -172,7 +172,7 @@ function dispatch(action: Action) {
172
172
 
173
173
  type Toast = Omit<ToasterToast, "id">
174
174
 
175
- function toast({ duration = TOAST_DEFAULT_DURATION, ...props }: Toast) {
175
+ function createToast({ duration = TOAST_DEFAULT_DURATION, ...props }: Toast) {
176
176
  const id = genId()
177
177
 
178
178
  const update = (props: ToasterToast) =>
@@ -206,6 +206,24 @@ function toast({ duration = TOAST_DEFAULT_DURATION, ...props }: Toast) {
206
206
  }
207
207
  }
208
208
 
209
+ // Main toast function with variant helpers
210
+ function toast(props: Toast) {
211
+ return createToast(props)
212
+ }
213
+
214
+ // Convenience methods for different variants
215
+ toast.success = (props: Omit<Toast, "variant">) =>
216
+ createToast({ ...props, variant: "success" })
217
+
218
+ toast.error = (props: Omit<Toast, "variant">) =>
219
+ createToast({ ...props, variant: "destructive" })
220
+
221
+ toast.warning = (props: Omit<Toast, "variant">) =>
222
+ createToast({ ...props, variant: "warning" })
223
+
224
+ toast.info = (props: Omit<Toast, "variant">) =>
225
+ createToast({ ...props, variant: "info" })
226
+
209
227
  function useToast() {
210
228
  const [state, setState] = React.useState<State>(memoryState)
211
229