@djangocfg/ui-core 2.1.412 → 2.1.415
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 +4 -4
- package/src/components/data/avatar-group/index.tsx +224 -0
- package/src/components/data/badge-overflow/index.tsx +259 -0
- package/src/components/data/circular-progress/index.tsx +358 -0
- package/src/components/data/relative-time-card/index.tsx +191 -0
- package/src/components/data/stat/index.tsx +140 -0
- package/src/components/data/status/index.tsx +80 -0
- package/src/components/effects/GlowBackground.tsx +9 -1
- package/src/components/effects/swap/index.tsx +289 -0
- package/src/components/feedback/banner/index.tsx +693 -0
- package/src/components/forms/checkbox-group/index.tsx +243 -0
- package/src/components/forms/editable/index.tsx +420 -0
- package/src/components/forms/input-otp/index.tsx +12 -3
- package/src/components/forms/mask-input/index.tsx +466 -0
- package/src/components/forms/otp/index.tsx +12 -8
- package/src/components/forms/segmented-input/index.tsx +319 -0
- package/src/components/forms/tags-input/index.tsx +896 -0
- package/src/components/forms/time-picker/index.tsx +285 -0
- package/src/components/index.ts +51 -0
- package/src/components/layout/key-value/index.tsx +884 -0
- package/src/components/layout/stack/index.tsx +349 -0
- package/src/components/navigation/context-menu/index.tsx +9 -6
- package/src/components/navigation/stepper/index.tsx +1307 -0
- package/src/components/select/multi-select-pro-async.tsx +11 -2
- package/src/components/select/multi-select-pro.tsx +11 -2
- package/src/components/specialized/presence/index.tsx +181 -0
- package/src/components/specialized/primitive/index.tsx +83 -0
- package/src/components/specialized/visually-hidden/index.tsx +19 -0
- package/src/components/specialized/visually-hidden-input/index.tsx +99 -0
- package/src/hooks/dom/index.ts +4 -0
- package/src/hooks/dom/useFormReset.ts +49 -0
- package/src/hooks/dom/useLayoutEffect.ts +16 -0
- package/src/hooks/dom/useSize.ts +57 -0
- package/src/hooks/state/index.ts +4 -0
- package/src/hooks/state/useCallbackRef.ts +25 -0
- package/src/hooks/state/usePrevious.ts +20 -0
- package/src/hooks/state/useStateMachine.ts +29 -0
- package/src/lib/compose-event-handlers.ts +22 -0
- package/src/lib/compose-refs.ts +65 -0
- package/src/lib/create-context.tsx +62 -0
- package/src/lib/get-element-ref.ts +33 -0
- package/src/lib/index.ts +5 -0
- package/src/lib/styles.ts +103 -0
- package/src/styles/README.md +43 -0
- package/src/styles/palette/utils.ts +15 -5
- package/src/styles/utilities/animations.css +135 -0
- package/src/styles/utilities/display.css +62 -0
- package/src/styles/utilities/glass.css +57 -0
- package/src/styles/utilities/marquee.css +69 -0
- package/src/styles/utilities/step.css +25 -0
- 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-
|
|
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
|
|
89
|
-
"
|
|
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
|
|
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
|
|
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-
|
|
184
|
+
"ml-auto pl-4 text-xs tabular-nums tracking-wide text-muted-foreground",
|
|
182
185
|
className
|
|
183
186
|
)}
|
|
184
187
|
{...props}
|