@djangocfg/ui-core 2.1.411 → 2.1.413

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.
Files changed (52) hide show
  1. package/package.json +4 -4
  2. package/src/components/data/avatar-group/index.tsx +224 -0
  3. package/src/components/data/badge-overflow/index.tsx +259 -0
  4. package/src/components/data/circular-progress/index.tsx +358 -0
  5. package/src/components/data/relative-time-card/index.tsx +191 -0
  6. package/src/components/data/stat/index.tsx +140 -0
  7. package/src/components/data/status/index.tsx +80 -0
  8. package/src/components/effects/GlowBackground.tsx +9 -1
  9. package/src/components/effects/swap/index.tsx +289 -0
  10. package/src/components/feedback/banner/index.tsx +693 -0
  11. package/src/components/forms/checkbox-group/index.tsx +243 -0
  12. package/src/components/forms/editable/index.tsx +420 -0
  13. package/src/components/forms/input-otp/index.tsx +12 -3
  14. package/src/components/forms/mask-input/index.tsx +466 -0
  15. package/src/components/forms/otp/index.tsx +12 -8
  16. package/src/components/forms/segmented-input/index.tsx +319 -0
  17. package/src/components/forms/tags-input/index.tsx +896 -0
  18. package/src/components/forms/time-picker/index.tsx +285 -0
  19. package/src/components/index.ts +51 -0
  20. package/src/components/layout/key-value/index.tsx +884 -0
  21. package/src/components/layout/stack/index.tsx +349 -0
  22. package/src/components/navigation/context-menu/index.tsx +9 -6
  23. package/src/components/navigation/stepper/index.tsx +1307 -0
  24. package/src/components/select/multi-select-pro-async.tsx +11 -2
  25. package/src/components/select/multi-select-pro.tsx +11 -2
  26. package/src/components/select/select.tsx +13 -3
  27. package/src/components/specialized/presence/index.tsx +181 -0
  28. package/src/components/specialized/primitive/index.tsx +83 -0
  29. package/src/components/specialized/visually-hidden/index.tsx +19 -0
  30. package/src/components/specialized/visually-hidden-input/index.tsx +99 -0
  31. package/src/hooks/dom/index.ts +4 -0
  32. package/src/hooks/dom/useFormReset.ts +49 -0
  33. package/src/hooks/dom/useLayoutEffect.ts +16 -0
  34. package/src/hooks/dom/useSize.ts +57 -0
  35. package/src/hooks/state/index.ts +4 -0
  36. package/src/hooks/state/useCallbackRef.ts +25 -0
  37. package/src/hooks/state/usePrevious.ts +20 -0
  38. package/src/hooks/state/useStateMachine.ts +29 -0
  39. package/src/lib/compose-event-handlers.ts +22 -0
  40. package/src/lib/compose-refs.ts +65 -0
  41. package/src/lib/create-context.tsx +62 -0
  42. package/src/lib/get-element-ref.ts +33 -0
  43. package/src/lib/index.ts +5 -0
  44. package/src/lib/styles.ts +103 -0
  45. package/src/styles/README.md +43 -0
  46. package/src/styles/palette/utils.ts +15 -5
  47. package/src/styles/utilities/animations.css +135 -0
  48. package/src/styles/utilities/display.css +62 -0
  49. package/src/styles/utilities/glass.css +57 -0
  50. package/src/styles/utilities/marquee.css +69 -0
  51. package/src/styles/utilities/step.css +25 -0
  52. package/src/styles/utilities.css +6 -259
@@ -0,0 +1,349 @@
1
+ "use client"
2
+
3
+ import { cva } from "class-variance-authority"
4
+ import * as React from "react"
5
+ import { Slot } from "@radix-ui/react-slot"
6
+
7
+ import { cn } from "../../../lib/utils"
8
+
9
+ interface ItemDimension {
10
+ itemId: number
11
+ size: number
12
+ }
13
+
14
+ type Side = "top" | "bottom"
15
+
16
+ function getDataState(isExpanded: boolean) {
17
+ return isExpanded ? "expanded" : "collapsed"
18
+ }
19
+
20
+ interface StackContextValue {
21
+ side: Side
22
+ childrenCount: number
23
+ itemCount: number
24
+ expandedItemCount: number
25
+ gap: number
26
+ scale: number
27
+ offset: number
28
+ expandOnHover: boolean
29
+ isExpanded: boolean
30
+ isInteracting: boolean
31
+ dimensions: ItemDimension[]
32
+ setDimensions: React.Dispatch<React.SetStateAction<ItemDimension[]>>
33
+ }
34
+
35
+ const StackContext = React.createContext<StackContextValue | null>(null)
36
+
37
+ function useStackContext(consumerName: string) {
38
+ const context = React.useContext(StackContext)
39
+ if (!context) {
40
+ throw new Error(`\`${consumerName}\` must be used within \`Stack\``)
41
+ }
42
+ return context
43
+ }
44
+
45
+ interface StackProps extends React.ComponentProps<"div"> {
46
+ side?: Side
47
+ itemCount?: number
48
+ expandedItemCount?: number
49
+ gap?: number
50
+ scale?: number
51
+ offset?: number
52
+ expandOnHover?: boolean
53
+ asChild?: boolean
54
+ }
55
+
56
+ function Stack(props: StackProps) {
57
+ const {
58
+ side = "bottom",
59
+ itemCount = 3,
60
+ expandedItemCount,
61
+ gap = 8,
62
+ scale = 0.05,
63
+ offset = 10,
64
+ className,
65
+ children,
66
+ style,
67
+ onMouseEnter: onMouseEnterProp,
68
+ onMouseLeave: onMouseLeaveProp,
69
+ onMouseMove: onMouseMoveProp,
70
+ onPointerDown: onPointerDownProp,
71
+ onPointerUp: onPointerUpProp,
72
+ expandOnHover = false,
73
+ asChild,
74
+ ...rootProps
75
+ } = props
76
+
77
+ const [isExpanded, setIsExpanded] = React.useState(false)
78
+ const [isInteracting, setIsInteracting] = React.useState(false)
79
+ const [dimensions, setDimensions] = React.useState<ItemDimension[]>([])
80
+
81
+ const childrenArray = React.Children.toArray(children).filter(
82
+ React.isValidElement,
83
+ )
84
+ const childrenCount = childrenArray.length
85
+
86
+ const effectiveExpandedItemCount = expandedItemCount ?? childrenCount
87
+
88
+ const onMouseEnter = React.useCallback(
89
+ (event: React.MouseEvent<HTMLDivElement>) => {
90
+ onMouseEnterProp?.(event)
91
+ if (event.defaultPrevented) return
92
+
93
+ if (expandOnHover) {
94
+ setIsExpanded(true)
95
+ }
96
+ },
97
+ [expandOnHover, onMouseEnterProp],
98
+ )
99
+
100
+ const onMouseMove = React.useCallback(
101
+ (event: React.MouseEvent<HTMLDivElement>) => {
102
+ onMouseMoveProp?.(event)
103
+ if (event.defaultPrevented) return
104
+
105
+ if (expandOnHover) {
106
+ setIsExpanded(true)
107
+ }
108
+ },
109
+ [expandOnHover, onMouseMoveProp],
110
+ )
111
+
112
+ const onMouseLeave = React.useCallback(
113
+ (event: React.MouseEvent<HTMLDivElement>) => {
114
+ onMouseLeaveProp?.(event)
115
+ if (event.defaultPrevented) return
116
+
117
+ if (expandOnHover && !isInteracting) {
118
+ setIsExpanded(false)
119
+ }
120
+ },
121
+ [expandOnHover, isInteracting, onMouseLeaveProp],
122
+ )
123
+
124
+ const onPointerDown = React.useCallback(
125
+ (event: React.PointerEvent<HTMLDivElement>) => {
126
+ onPointerDownProp?.(event)
127
+ if (event.defaultPrevented) return
128
+
129
+ setIsInteracting(true)
130
+ },
131
+ [onPointerDownProp],
132
+ )
133
+
134
+ const onPointerUp = React.useCallback(
135
+ (event: React.PointerEvent<HTMLDivElement>) => {
136
+ onPointerUpProp?.(event)
137
+ if (event.defaultPrevented) return
138
+
139
+ setIsInteracting(false)
140
+ },
141
+ [onPointerUpProp],
142
+ )
143
+
144
+ const contextValue = React.useMemo<StackContextValue>(
145
+ () => ({
146
+ side,
147
+ childrenCount,
148
+ itemCount,
149
+ expandedItemCount: effectiveExpandedItemCount,
150
+ gap,
151
+ scale,
152
+ offset,
153
+ expandOnHover,
154
+ isExpanded,
155
+ isInteracting,
156
+ dimensions,
157
+ setDimensions,
158
+ }),
159
+ [
160
+ side,
161
+ childrenCount,
162
+ itemCount,
163
+ effectiveExpandedItemCount,
164
+ gap,
165
+ scale,
166
+ offset,
167
+ expandOnHover,
168
+ isExpanded,
169
+ isInteracting,
170
+ dimensions,
171
+ ],
172
+ )
173
+
174
+ const RootPrimitive = asChild ? Slot : "div"
175
+
176
+ return (
177
+ <StackContext.Provider value={contextValue}>
178
+ <RootPrimitive
179
+ data-slot="stack"
180
+ data-state={getDataState(isExpanded)}
181
+ onMouseEnter={onMouseEnter}
182
+ onMouseMove={onMouseMove}
183
+ onMouseLeave={onMouseLeave}
184
+ onPointerDown={onPointerDown}
185
+ onPointerUp={onPointerUp}
186
+ {...rootProps}
187
+ className={cn("relative w-full", className)}
188
+ style={
189
+ {
190
+ "--gap": `${gap}px`,
191
+ "--offset": `${offset}px`,
192
+ "--scale": scale,
193
+ ...style,
194
+ } as React.CSSProperties
195
+ }
196
+ >
197
+ {childrenArray.map((child, index) => (
198
+ <StackItemWrapper key={index} index={index}>
199
+ {child}
200
+ </StackItemWrapper>
201
+ ))}
202
+ </RootPrimitive>
203
+ </StackContext.Provider>
204
+ )
205
+ }
206
+
207
+ const stackItemWrapperVariants = cva(
208
+ "absolute w-full transition-all duration-300 ease-out",
209
+ {
210
+ variants: {
211
+ side: {
212
+ top: [
213
+ "top-0 left-0 origin-top",
214
+ "translate-y-[calc(var(--translate)*-1)] scale-[var(--item-scale)]",
215
+ "after:absolute after:top-full after:left-0 after:w-full after:content-['']",
216
+ ],
217
+ bottom: [
218
+ "bottom-0 left-0 origin-bottom",
219
+ "translate-y-[var(--translate)] scale-[var(--item-scale)]",
220
+ "after:absolute after:bottom-full after:left-0 after:w-full after:content-['']",
221
+ ],
222
+ },
223
+ isExpanded: {
224
+ true: "after:h-[calc(var(--gap)+1px)]",
225
+ false: "",
226
+ },
227
+ isVisible: {
228
+ true: "",
229
+ false: "pointer-events-none",
230
+ },
231
+ },
232
+ },
233
+ )
234
+
235
+ type StackItemWrapperElement = HTMLDivElement
236
+
237
+ interface StackItemWrapperProps extends React.ComponentProps<"div"> {
238
+ index: number
239
+ }
240
+
241
+ function StackItemWrapper(props: StackItemWrapperProps) {
242
+ const { children, className, index, style, ...itemProps } = props
243
+
244
+ const {
245
+ side,
246
+ childrenCount,
247
+ itemCount,
248
+ expandedItemCount,
249
+ gap,
250
+ scale,
251
+ offset,
252
+ isExpanded,
253
+ dimensions,
254
+ setDimensions,
255
+ } = useStackContext("StackItemWrapper")
256
+
257
+ const itemRef = React.useRef<StackItemWrapperElement>(null)
258
+
259
+ const isFront = index === 0
260
+ const isVisible = isExpanded ? index < expandedItemCount : index < itemCount
261
+
262
+ React.useEffect(() => {
263
+ const itemNode = itemRef.current
264
+ if (itemNode) {
265
+ const rect = itemNode.getBoundingClientRect()
266
+ const measuredHeight = rect.height
267
+ const currentScale = 1 - index * scale
268
+ const naturalHeight = measuredHeight / currentScale
269
+
270
+ setDimensions((d) => {
271
+ const existing = d.find((item) => item.itemId === index)
272
+ if (!existing) {
273
+ return [...d, { itemId: index, size: naturalHeight }]
274
+ }
275
+ return d
276
+ })
277
+ }
278
+ }, [index, scale, setDimensions])
279
+
280
+ const itemsSizeBefore = React.useMemo(() => {
281
+ return dimensions.reduce((prev, curr) => {
282
+ if (curr.itemId >= index) return prev
283
+ return prev + curr.size
284
+ }, 0)
285
+ }, [dimensions, index])
286
+
287
+ const itemScale = isExpanded ? 1 : 1 - index * scale
288
+ const translateValue = isExpanded
289
+ ? index * gap + itemsSizeBefore
290
+ : index * offset
291
+ const zIndex = childrenCount - index
292
+
293
+ const opacity = !isVisible ? 0 : isExpanded ? 1 : 1 - index * 0.15
294
+
295
+ return (
296
+ <div
297
+ ref={itemRef}
298
+ data-slot="stack-item-wrapper"
299
+ data-index={index}
300
+ data-front={isFront}
301
+ data-visible={isVisible}
302
+ data-expanded={isExpanded}
303
+ className={cn(
304
+ stackItemWrapperVariants({ side, isExpanded, isVisible, className }),
305
+ )}
306
+ style={
307
+ {
308
+ "--translate": `${translateValue}px`,
309
+ "--item-scale": itemScale,
310
+ zIndex,
311
+ opacity,
312
+ ...style,
313
+ } as React.CSSProperties
314
+ }
315
+ {...itemProps}
316
+ >
317
+ <div
318
+ data-index={index}
319
+ data-position={isFront ? "front" : "back"}
320
+ data-state={getDataState(isExpanded)}
321
+ >
322
+ {children}
323
+ </div>
324
+ </div>
325
+ )
326
+ }
327
+
328
+ interface StackItemProps extends React.ComponentProps<"div"> {
329
+ asChild?: boolean
330
+ }
331
+
332
+ function StackItem(props: StackItemProps) {
333
+ const { asChild, className, ...itemProps } = props
334
+
335
+ const ItemPrimitive = asChild ? Slot : "div"
336
+
337
+ return (
338
+ <ItemPrimitive
339
+ data-slot="stack-item"
340
+ {...itemProps}
341
+ className={cn(
342
+ "rounded-lg border bg-card p-4 shadow-sm transition-shadow duration-200 hover:shadow-md",
343
+ className,
344
+ )}
345
+ />
346
+ )
347
+ }
348
+
349
+ export { Stack, StackItem, type StackProps }
@@ -63,7 +63,7 @@ const ContextMenuContent = React.forwardRef<
63
63
  <ContextMenuPrimitive.Content
64
64
  ref={ref}
65
65
  className={cn(
66
- "z-[700] max-h-[--radix-context-menu-content-available-height] min-w-32 overflow-y-auto overflow-x-hidden rounded-[var(--radius-popover)] border bg-popover backdrop-blur-xl p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
66
+ "z-[700] max-h-[--radix-context-menu-content-available-height] min-w-[8rem] overflow-y-auto overflow-x-hidden rounded-[var(--radius-popover)] border bg-popover backdrop-blur-xl p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
67
67
  className
68
68
  )}
69
69
  {...props}
@@ -85,8 +85,9 @@ const ContextMenuItem = React.forwardRef<
85
85
  ref={ref}
86
86
  data-variant={variant}
87
87
  className={cn(
88
- "relative flex cursor-default select-none items-center rounded-[var(--radius-sm)] px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
89
- "data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive",
88
+ "relative flex cursor-default select-none items-center gap-2 rounded-[var(--radius-sm)] px-2 py-1 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
89
+ "[&_svg]:size-4 [&_svg]:shrink-0 [&_svg]:text-muted-foreground",
90
+ "data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive [&[data-variant=destructive]_svg]:text-destructive",
90
91
  inset && "pl-8",
91
92
  className
92
93
  )}
@@ -102,7 +103,8 @@ const ContextMenuCheckboxItem = React.forwardRef<
102
103
  <ContextMenuPrimitive.CheckboxItem
103
104
  ref={ref}
104
105
  className={cn(
105
- "relative flex cursor-default select-none items-center rounded-[var(--radius-sm)] py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
106
+ "relative flex cursor-default select-none items-center gap-2 rounded-[var(--radius-sm)] py-1 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
107
+ "[&_svg]:size-4 [&_svg]:shrink-0",
106
108
  className
107
109
  )}
108
110
  checked={checked}
@@ -126,7 +128,8 @@ const ContextMenuRadioItem = React.forwardRef<
126
128
  <ContextMenuPrimitive.RadioItem
127
129
  ref={ref}
128
130
  className={cn(
129
- "relative flex cursor-default select-none items-center rounded-[var(--radius-sm)] py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
131
+ "relative flex cursor-default select-none items-center gap-2 rounded-[var(--radius-sm)] py-1 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
132
+ "[&_svg]:size-4 [&_svg]:shrink-0",
130
133
  className
131
134
  )}
132
135
  {...props}
@@ -178,7 +181,7 @@ const ContextMenuShortcut = ({
178
181
  return (
179
182
  <span
180
183
  className={cn(
181
- "ml-auto text-xs tracking-widest text-muted-foreground",
184
+ "ml-auto pl-4 text-xs tabular-nums tracking-wide text-muted-foreground",
182
185
  className
183
186
  )}
184
187
  {...props}