@djangocfg/ui-core 2.1.264 → 2.1.267

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.264",
3
+ "version": "2.1.267",
4
4
  "description": "Pure React UI component library without Next.js dependencies - for Electron, Vite, CRA apps",
5
5
  "keywords": [
6
6
  "ui-components",
@@ -86,7 +86,7 @@
86
86
  "playground": "playground dev"
87
87
  },
88
88
  "peerDependencies": {
89
- "@djangocfg/i18n": "^2.1.264",
89
+ "@djangocfg/i18n": "^2.1.267",
90
90
  "consola": "^3.4.2",
91
91
  "lucide-react": "^0.545.0",
92
92
  "moment": "^2.30.1",
@@ -148,9 +148,9 @@
148
148
  "vaul": "1.1.2"
149
149
  },
150
150
  "devDependencies": {
151
- "@djangocfg/i18n": "^2.1.264",
151
+ "@djangocfg/i18n": "^2.1.267",
152
152
  "@djangocfg/playground": "workspace:*",
153
- "@djangocfg/typescript-config": "^2.1.264",
153
+ "@djangocfg/typescript-config": "^2.1.267",
154
154
  "@types/node": "^24.7.2",
155
155
  "@types/react": "^19.1.0",
156
156
  "@types/react-dom": "^19.1.0",
@@ -185,9 +185,23 @@ const Tabs = React.forwardRef<
185
185
  })
186
186
  Tabs.displayName = "Tabs"
187
187
 
188
+ // ─────────────────────────────────────────────────────────────────────────
189
+ // Tabs variant context — lets TabsTrigger inherit variant from TabsList
190
+ // ─────────────────────────────────────────────────────────────────────────
191
+
192
+ type TabsVariant = 'default' | 'underline'
193
+ const TabsVariantContext = React.createContext<TabsVariant>('default')
194
+
188
195
  const TabsList = React.forwardRef<
189
196
  React.ElementRef<typeof TabsPrimitive.List>,
190
197
  React.ComponentPropsWithoutRef<typeof TabsPrimitive.List> & {
198
+ /**
199
+ * Visual variant.
200
+ * - `default` — pill/card style (bg-muted container)
201
+ * - `underline` — borderless, underline indicator on active tab
202
+ * @default 'default'
203
+ */
204
+ variant?: TabsVariant
191
205
  /**
192
206
  * Full width mode - tabs will stretch equally to fill the container
193
207
  * @default false
@@ -199,36 +213,41 @@ const TabsList = React.forwardRef<
199
213
  */
200
214
  scrollable?: boolean
201
215
  }
202
- >(({ className, fullWidth = false, scrollable = false, ...props }, ref) => {
203
- // If scrollable mode is enabled, wrap in ScrollArea
204
- if (scrollable) {
205
- return (
206
- <ScrollArea className="w-full whitespace-nowrap rounded-lg">
207
- <TabsPrimitive.List
208
- ref={ref}
209
- className={cn(
210
- "inline-flex h-9 items-center justify-center rounded-lg bg-muted p-1 text-muted-foreground",
211
- fullWidth && "w-full flex",
212
- className
213
- )}
214
- {...props}
215
- />
216
- <ScrollBar orientation="horizontal" />
217
- </ScrollArea>
218
- )
219
- }
216
+ >(({ className, variant = 'default', fullWidth = false, scrollable = false, ...props }, ref) => {
217
+ const isUnderline = variant === 'underline'
218
+
219
+ const listCls = cn(
220
+ isUnderline
221
+ ? "inline-flex items-end gap-0 bg-transparent p-0 h-auto rounded-none"
222
+ : "inline-flex h-9 items-center justify-center rounded-lg bg-muted p-1 text-muted-foreground",
223
+ fullWidth && "w-full flex",
224
+ className
225
+ )
226
+
227
+ const list = (
228
+ <TabsPrimitive.List ref={ref} className={listCls} {...props} />
229
+ )
230
+
231
+ // For underline variant — native overflow-x-auto, full-width border via absolute element
232
+ const wrapped = isUnderline ? (
233
+ <div
234
+ className={cn("relative w-full", scrollable && "overflow-x-auto")}
235
+ style={scrollable ? { scrollbarWidth: 'none', msOverflowStyle: 'none' } as React.CSSProperties : undefined}
236
+ >
237
+ <div className="absolute bottom-0 left-0 right-0 h-px bg-border" />
238
+ {list}
239
+ </div>
240
+ ) : scrollable ? (
241
+ <ScrollArea className="w-full whitespace-nowrap">
242
+ {list}
243
+ <ScrollBar orientation="horizontal" />
244
+ </ScrollArea>
245
+ ) : list
220
246
 
221
- // Default mode without scrolling
222
247
  return (
223
- <TabsPrimitive.List
224
- ref={ref}
225
- className={cn(
226
- "inline-flex h-9 items-center justify-center rounded-lg bg-muted p-1 text-muted-foreground",
227
- fullWidth && "w-full flex",
228
- className
229
- )}
230
- {...props}
231
- />
248
+ <TabsVariantContext.Provider value={variant}>
249
+ {wrapped}
250
+ </TabsVariantContext.Provider>
232
251
  )
233
252
  })
234
253
  TabsList.displayName = TabsPrimitive.List.displayName
@@ -243,17 +262,41 @@ const TabsTrigger = React.forwardRef<
243
262
  flexEqual?: boolean
244
263
  key?: React.Key
245
264
  }
246
- >(({ className, flexEqual = false, ...props }, ref) => (
247
- <TabsPrimitive.Trigger
248
- ref={ref}
249
- className={cn(
250
- "inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow",
251
- flexEqual && "flex-1",
252
- className
253
- )}
254
- {...props}
255
- />
256
- ))
265
+ >(({ className, flexEqual = false, ...props }, ref) => {
266
+ const variant = React.useContext(TabsVariantContext)
267
+ const isUnderline = variant === 'underline'
268
+
269
+ return (
270
+ <TabsPrimitive.Trigger
271
+ ref={ref}
272
+ className={cn(
273
+ isUnderline
274
+ ? [
275
+ "relative z-10 rounded-none bg-transparent shadow-none whitespace-nowrap",
276
+ "px-3 py-2.5 mb-[-1px]",
277
+ "text-sm font-medium text-muted-foreground/60",
278
+ "border-b-2 border-transparent",
279
+ "transition-all duration-150",
280
+ "hover:text-foreground",
281
+ "focus-visible:outline-none",
282
+ "disabled:pointer-events-none disabled:opacity-50",
283
+ "data-[state=active]:text-foreground data-[state=active]:font-semibold",
284
+ "data-[state=active]:border-foreground data-[state=active]:shadow-none",
285
+ ].join(" ")
286
+ : [
287
+ "inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1",
288
+ "text-sm font-medium ring-offset-background transition-all",
289
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
290
+ "disabled:pointer-events-none disabled:opacity-50",
291
+ "data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow",
292
+ ].join(" "),
293
+ flexEqual && "flex-1",
294
+ className
295
+ )}
296
+ {...props}
297
+ />
298
+ )
299
+ })
257
300
  TabsTrigger.displayName = TabsPrimitive.Trigger.displayName
258
301
 
259
302
  const TabsContent = React.forwardRef<