@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,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 }