@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,80 @@
|
|
|
1
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
2
|
+
import * as React from "react"
|
|
3
|
+
import { Slot } from "@radix-ui/react-slot"
|
|
4
|
+
|
|
5
|
+
import { cn } from "../../../lib/utils"
|
|
6
|
+
|
|
7
|
+
const statusVariants = cva(
|
|
8
|
+
"inline-flex w-fit shrink-0 items-center gap-1.5 overflow-hidden whitespace-nowrap rounded-full border px-2.5 py-1 font-medium text-xs transition-colors",
|
|
9
|
+
{
|
|
10
|
+
variants: {
|
|
11
|
+
variant: {
|
|
12
|
+
default:
|
|
13
|
+
"border-transparent bg-muted text-muted-foreground **:data-[slot=status-indicator]:bg-muted-foreground",
|
|
14
|
+
success:
|
|
15
|
+
"border-green-500/20 bg-green-500/10 text-green-600 **:data-[slot=status-indicator]:bg-green-600 dark:text-green-400 **:data-[slot=status-indicator]:dark:bg-green-400",
|
|
16
|
+
error:
|
|
17
|
+
"border-destructive/20 bg-destructive/10 text-destructive **:data-[slot=status-indicator]:bg-destructive",
|
|
18
|
+
warning:
|
|
19
|
+
"border-orange-500/20 bg-orange-500/10 text-orange-600 **:data-[slot=status-indicator]:bg-orange-600 dark:text-orange-400 **:data-[slot=status-indicator]:dark:bg-orange-400",
|
|
20
|
+
info: "border-blue-500/20 bg-blue-500/10 text-blue-600 **:data-[slot=status-indicator]:bg-blue-600 dark:text-blue-400 **:data-[slot=status-indicator]:dark:bg-blue-400",
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
defaultVariants: {
|
|
24
|
+
variant: "default",
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
interface StatusProps
|
|
30
|
+
extends VariantProps<typeof statusVariants>,
|
|
31
|
+
React.ComponentProps<"div"> {
|
|
32
|
+
asChild?: boolean
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function Status(props: StatusProps) {
|
|
36
|
+
const { className, variant = "default", asChild, ...rootProps } = props
|
|
37
|
+
|
|
38
|
+
const RootPrimitive = asChild ? Slot : "div"
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<RootPrimitive
|
|
42
|
+
data-slot="status"
|
|
43
|
+
data-variant={variant}
|
|
44
|
+
{...rootProps}
|
|
45
|
+
className={cn(statusVariants({ variant }), className)}
|
|
46
|
+
/>
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function StatusIndicator(props: React.ComponentProps<"div">) {
|
|
51
|
+
const { className, ...indicatorProps } = props
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<div
|
|
55
|
+
data-slot="status-indicator"
|
|
56
|
+
{...indicatorProps}
|
|
57
|
+
className={cn(
|
|
58
|
+
"relative flex size-2 shrink-0 rounded-full",
|
|
59
|
+
"before:absolute before:inset-0 before:animate-ping before:rounded-full before:bg-inherit",
|
|
60
|
+
"after:absolute after:inset-[2px] after:rounded-full after:bg-inherit",
|
|
61
|
+
className,
|
|
62
|
+
)}
|
|
63
|
+
/>
|
|
64
|
+
)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function StatusLabel(props: React.ComponentProps<"div">) {
|
|
68
|
+
const { className, ...labelProps } = props
|
|
69
|
+
|
|
70
|
+
return (
|
|
71
|
+
<div
|
|
72
|
+
data-slot="status-label"
|
|
73
|
+
{...labelProps}
|
|
74
|
+
className={cn("leading-none", className)}
|
|
75
|
+
/>
|
|
76
|
+
)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export { Status, StatusIndicator, StatusLabel, statusVariants }
|
|
80
|
+
export type { StatusProps }
|
|
@@ -4,6 +4,13 @@ import * as React from 'react';
|
|
|
4
4
|
|
|
5
5
|
export interface GlowBackgroundProps {
|
|
6
6
|
className?: string;
|
|
7
|
+
/**
|
|
8
|
+
* Overall opacity of the glow layer (0..1). Use ≤0.5 when the glow
|
|
9
|
+
* sits behind dense UI like forms — the default `1` is tuned for
|
|
10
|
+
* marketing surfaces and quickly drowns small inputs.
|
|
11
|
+
* @default 1
|
|
12
|
+
*/
|
|
13
|
+
intensity?: number;
|
|
7
14
|
}
|
|
8
15
|
|
|
9
16
|
/**
|
|
@@ -20,7 +27,7 @@ export interface GlowBackgroundProps {
|
|
|
20
27
|
* </div>
|
|
21
28
|
* ```
|
|
22
29
|
*/
|
|
23
|
-
export const GlowBackground = React.memo(({ className }: GlowBackgroundProps) => (
|
|
30
|
+
export const GlowBackground = React.memo(({ className, intensity = 1 }: GlowBackgroundProps) => (
|
|
24
31
|
<div
|
|
25
32
|
aria-hidden="true"
|
|
26
33
|
style={{
|
|
@@ -29,6 +36,7 @@ export const GlowBackground = React.memo(({ className }: GlowBackgroundProps) =>
|
|
|
29
36
|
overflow: 'hidden',
|
|
30
37
|
pointerEvents: 'none',
|
|
31
38
|
zIndex: 0,
|
|
39
|
+
opacity: intensity,
|
|
32
40
|
}}
|
|
33
41
|
className={className}
|
|
34
42
|
>
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { Slot } from "@radix-ui/react-slot"
|
|
5
|
+
|
|
6
|
+
import { cn } from "../../../lib/utils"
|
|
7
|
+
|
|
8
|
+
interface DivProps extends React.ComponentProps<"div"> {
|
|
9
|
+
asChild?: boolean
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function getDataState(swapped: boolean) {
|
|
13
|
+
return swapped ? "on" : "off"
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface StoreState {
|
|
17
|
+
swapped: boolean
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface Store {
|
|
21
|
+
subscribe: (callback: () => void) => () => void
|
|
22
|
+
getState: () => StoreState
|
|
23
|
+
setState: <K extends keyof StoreState>(key: K, value: StoreState[K]) => void
|
|
24
|
+
notify: () => void
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const StoreContext = React.createContext<Store | null>(null)
|
|
28
|
+
|
|
29
|
+
function useStore<T>(
|
|
30
|
+
selector: (state: StoreState) => T,
|
|
31
|
+
ogStore?: Store | null,
|
|
32
|
+
): T {
|
|
33
|
+
const contextStore = React.useContext(StoreContext)
|
|
34
|
+
|
|
35
|
+
const store = ogStore ?? contextStore
|
|
36
|
+
|
|
37
|
+
if (!store) {
|
|
38
|
+
throw new Error(`\`useStore\` must be used within \`Swap\``)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const getSnapshot = React.useCallback(
|
|
42
|
+
() => selector(store.getState()),
|
|
43
|
+
[store, selector],
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
return React.useSyncExternalStore(store.subscribe, getSnapshot, getSnapshot)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
interface SwapProps extends DivProps {
|
|
50
|
+
swapped?: boolean
|
|
51
|
+
defaultSwapped?: boolean
|
|
52
|
+
onSwappedChange?: (swapped: boolean) => void
|
|
53
|
+
activationMode?: "click" | "hover"
|
|
54
|
+
animation?: "fade" | "rotate" | "flip" | "scale"
|
|
55
|
+
disabled?: boolean
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function Swap(props: SwapProps) {
|
|
59
|
+
const {
|
|
60
|
+
swapped: swappedProp,
|
|
61
|
+
defaultSwapped,
|
|
62
|
+
onSwappedChange,
|
|
63
|
+
activationMode = "click",
|
|
64
|
+
animation = "fade",
|
|
65
|
+
disabled,
|
|
66
|
+
asChild,
|
|
67
|
+
className,
|
|
68
|
+
onClick: onClickProp,
|
|
69
|
+
onMouseEnter: onMouseEnterProp,
|
|
70
|
+
onMouseLeave: onMouseLeaveProp,
|
|
71
|
+
onKeyDown: onKeyDownProp,
|
|
72
|
+
...rootProps
|
|
73
|
+
} = props
|
|
74
|
+
|
|
75
|
+
const listenersRef = React.useRef<Set<() => void>>(new Set())
|
|
76
|
+
const stateRef = React.useRef<StoreState>({
|
|
77
|
+
swapped: swappedProp ?? defaultSwapped ?? false,
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
const propsRef = React.useRef({
|
|
81
|
+
activationMode,
|
|
82
|
+
animation,
|
|
83
|
+
disabled,
|
|
84
|
+
onSwappedChange,
|
|
85
|
+
onClick: onClickProp,
|
|
86
|
+
onMouseEnter: onMouseEnterProp,
|
|
87
|
+
onMouseLeave: onMouseLeaveProp,
|
|
88
|
+
onKeyDown: onKeyDownProp,
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
React.useEffect(() => {
|
|
92
|
+
propsRef.current = {
|
|
93
|
+
activationMode,
|
|
94
|
+
animation,
|
|
95
|
+
disabled,
|
|
96
|
+
onSwappedChange,
|
|
97
|
+
onClick: onClickProp,
|
|
98
|
+
onMouseEnter: onMouseEnterProp,
|
|
99
|
+
onMouseLeave: onMouseLeaveProp,
|
|
100
|
+
onKeyDown: onKeyDownProp,
|
|
101
|
+
}
|
|
102
|
+
}, [
|
|
103
|
+
activationMode,
|
|
104
|
+
animation,
|
|
105
|
+
disabled,
|
|
106
|
+
onSwappedChange,
|
|
107
|
+
onClickProp,
|
|
108
|
+
onMouseEnterProp,
|
|
109
|
+
onMouseLeaveProp,
|
|
110
|
+
onKeyDownProp,
|
|
111
|
+
])
|
|
112
|
+
|
|
113
|
+
const isClickMode = activationMode === "click"
|
|
114
|
+
|
|
115
|
+
const store = React.useMemo<Store>(() => {
|
|
116
|
+
return {
|
|
117
|
+
subscribe: (cb) => {
|
|
118
|
+
listenersRef.current.add(cb)
|
|
119
|
+
return () => listenersRef.current.delete(cb)
|
|
120
|
+
},
|
|
121
|
+
getState: () => stateRef.current,
|
|
122
|
+
setState: (key, value) => {
|
|
123
|
+
if (Object.is(stateRef.current[key], value)) return
|
|
124
|
+
|
|
125
|
+
if (key === "swapped" && typeof value === "boolean") {
|
|
126
|
+
stateRef.current.swapped = value
|
|
127
|
+
propsRef.current.onSwappedChange?.(value)
|
|
128
|
+
} else {
|
|
129
|
+
stateRef.current[key] = value
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
store.notify()
|
|
133
|
+
},
|
|
134
|
+
notify: () => {
|
|
135
|
+
for (const cb of listenersRef.current) {
|
|
136
|
+
cb()
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
}
|
|
140
|
+
}, [])
|
|
141
|
+
|
|
142
|
+
const swapped = useStore((state) => state.swapped, store)
|
|
143
|
+
|
|
144
|
+
React.useEffect(() => {
|
|
145
|
+
if (swappedProp !== undefined) {
|
|
146
|
+
store.setState("swapped", swappedProp)
|
|
147
|
+
}
|
|
148
|
+
}, [swappedProp, store])
|
|
149
|
+
|
|
150
|
+
const onToggle = React.useCallback(() => {
|
|
151
|
+
if (propsRef.current.disabled) return
|
|
152
|
+
|
|
153
|
+
store.setState("swapped", !store.getState().swapped)
|
|
154
|
+
}, [store])
|
|
155
|
+
|
|
156
|
+
const onClick = React.useCallback(
|
|
157
|
+
(event: React.MouseEvent<HTMLDivElement>) => {
|
|
158
|
+
propsRef.current.onClick?.(event)
|
|
159
|
+
if (event.defaultPrevented || propsRef.current.activationMode !== "click")
|
|
160
|
+
return
|
|
161
|
+
|
|
162
|
+
onToggle()
|
|
163
|
+
},
|
|
164
|
+
[onToggle],
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
const onMouseEnter = React.useCallback(
|
|
168
|
+
(event: React.MouseEvent<HTMLDivElement>) => {
|
|
169
|
+
propsRef.current.onMouseEnter?.(event)
|
|
170
|
+
if (
|
|
171
|
+
event.defaultPrevented ||
|
|
172
|
+
propsRef.current.activationMode !== "hover" ||
|
|
173
|
+
propsRef.current.disabled
|
|
174
|
+
)
|
|
175
|
+
return
|
|
176
|
+
|
|
177
|
+
store.setState("swapped", true)
|
|
178
|
+
},
|
|
179
|
+
[store],
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
const onMouseLeave = React.useCallback(
|
|
183
|
+
(event: React.MouseEvent<HTMLDivElement>) => {
|
|
184
|
+
propsRef.current.onMouseLeave?.(event)
|
|
185
|
+
if (
|
|
186
|
+
event.defaultPrevented ||
|
|
187
|
+
propsRef.current.activationMode !== "hover" ||
|
|
188
|
+
propsRef.current.disabled
|
|
189
|
+
)
|
|
190
|
+
return
|
|
191
|
+
|
|
192
|
+
store.setState("swapped", false)
|
|
193
|
+
},
|
|
194
|
+
[store],
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
const onKeyDown = React.useCallback(
|
|
198
|
+
(event: React.KeyboardEvent<HTMLDivElement>) => {
|
|
199
|
+
propsRef.current.onKeyDown?.(event)
|
|
200
|
+
if (
|
|
201
|
+
event.defaultPrevented ||
|
|
202
|
+
propsRef.current.activationMode !== "click" ||
|
|
203
|
+
propsRef.current.disabled
|
|
204
|
+
)
|
|
205
|
+
return
|
|
206
|
+
|
|
207
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
208
|
+
event.preventDefault()
|
|
209
|
+
onToggle()
|
|
210
|
+
}
|
|
211
|
+
},
|
|
212
|
+
[onToggle],
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
const RootPrimitive = asChild ? Slot : "div"
|
|
216
|
+
|
|
217
|
+
return (
|
|
218
|
+
<StoreContext.Provider value={store}>
|
|
219
|
+
<RootPrimitive
|
|
220
|
+
role={isClickMode ? "button" : undefined}
|
|
221
|
+
aria-pressed={isClickMode ? swapped : undefined}
|
|
222
|
+
aria-disabled={disabled}
|
|
223
|
+
data-slot="swap"
|
|
224
|
+
data-animation={animation}
|
|
225
|
+
data-state={getDataState(swapped)}
|
|
226
|
+
data-disabled={disabled ? "" : undefined}
|
|
227
|
+
tabIndex={isClickMode && !disabled ? 0 : undefined}
|
|
228
|
+
{...rootProps}
|
|
229
|
+
className={cn(
|
|
230
|
+
"relative inline-flex cursor-pointer select-none items-center justify-center data-disabled:cursor-not-allowed data-disabled:opacity-50",
|
|
231
|
+
className,
|
|
232
|
+
)}
|
|
233
|
+
onClick={onClick}
|
|
234
|
+
onMouseEnter={onMouseEnter}
|
|
235
|
+
onMouseLeave={onMouseLeave}
|
|
236
|
+
onKeyDown={onKeyDown}
|
|
237
|
+
/>
|
|
238
|
+
</StoreContext.Provider>
|
|
239
|
+
)
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function SwapOn(props: DivProps) {
|
|
243
|
+
const { asChild, className, ...onProps } = props
|
|
244
|
+
|
|
245
|
+
const swapped = useStore((state) => state.swapped)
|
|
246
|
+
|
|
247
|
+
const OnPrimitive = asChild ? Slot : "div"
|
|
248
|
+
|
|
249
|
+
return (
|
|
250
|
+
<OnPrimitive
|
|
251
|
+
data-slot="swap-on"
|
|
252
|
+
data-state={getDataState(swapped)}
|
|
253
|
+
{...onProps}
|
|
254
|
+
className={cn(
|
|
255
|
+
"transition-all duration-300 data-[state=off]:absolute data-[state=off]:opacity-0 data-[state=on]:opacity-100 motion-reduce:transition-none",
|
|
256
|
+
"[*[data-animation=rotate]_&]:data-[state=off]:rotate-180 [*[data-animation=rotate]_&]:data-[state=on]:rotate-0 motion-reduce:[*[data-animation=rotate]_&]:data-[state=off]:rotate-0",
|
|
257
|
+
"[*[data-animation=flip]_&]:data-[state=off]:transform-[rotateY(180deg)] [*[data-animation=flip]_&]:data-[state=on]:transform-[rotateY(0deg)] motion-reduce:[*[data-animation=flip]_&]:data-[state=off]:transform-[rotateY(0deg)]",
|
|
258
|
+
"[*[data-animation=scale]_&]:data-[state=off]:scale-0 [*[data-animation=scale]_&]:data-[state=on]:scale-100 motion-reduce:[*[data-animation=scale]_&]:data-[state=off]:scale-100",
|
|
259
|
+
className,
|
|
260
|
+
)}
|
|
261
|
+
/>
|
|
262
|
+
)
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function SwapOff(props: DivProps) {
|
|
266
|
+
const { asChild, className, ...offProps } = props
|
|
267
|
+
|
|
268
|
+
const swapped = useStore((state) => state.swapped)
|
|
269
|
+
|
|
270
|
+
const OffPrimitive = asChild ? Slot : "div"
|
|
271
|
+
|
|
272
|
+
return (
|
|
273
|
+
<OffPrimitive
|
|
274
|
+
data-slot="swap-off"
|
|
275
|
+
data-state={getDataState(swapped)}
|
|
276
|
+
{...offProps}
|
|
277
|
+
className={cn(
|
|
278
|
+
"transition-all duration-300 data-[state=on]:absolute data-[state=off]:opacity-100 data-[state=on]:opacity-0 motion-reduce:transition-none",
|
|
279
|
+
"[*[data-animation=rotate]_&]:data-[state=off]:rotate-0 [*[data-animation=rotate]_&]:data-[state=on]:rotate-180 motion-reduce:[*[data-animation=rotate]_&]:data-[state=on]:rotate-0",
|
|
280
|
+
"[*[data-animation=flip]_&]:data-[state=off]:transform-[rotateY(0deg)] [*[data-animation=flip]_&]:data-[state=on]:transform-[rotateY(180deg)] motion-reduce:[*[data-animation=flip]_&]:data-[state=on]:transform-[rotateY(0deg)]",
|
|
281
|
+
"[*[data-animation=scale]_&]:data-[state=off]:scale-100 [*[data-animation=scale]_&]:data-[state=on]:scale-0 motion-reduce:[*[data-animation=scale]_&]:data-[state=on]:scale-100",
|
|
282
|
+
className,
|
|
283
|
+
)}
|
|
284
|
+
/>
|
|
285
|
+
)
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
export { Swap, SwapOff, SwapOn, useStore as useSwap }
|
|
289
|
+
export type { SwapProps }
|