@object-ui/components 3.0.3 → 3.1.1
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/.turbo/turbo-build.log +12 -12
- package/CHANGELOG.md +9 -0
- package/dist/index.css +1 -1
- package/dist/index.js +24932 -23139
- package/dist/index.umd.cjs +37 -37
- package/dist/src/custom/config-field-renderer.d.ts +21 -0
- package/dist/src/custom/config-panel-renderer.d.ts +81 -0
- package/dist/src/custom/config-row.d.ts +27 -0
- package/dist/src/custom/filter-builder.d.ts +1 -1
- package/dist/src/custom/index.d.ts +5 -0
- package/dist/src/custom/mobile-dialog-content.d.ts +20 -0
- package/dist/src/custom/navigation-overlay.d.ts +8 -0
- package/dist/src/custom/section-header.d.ts +31 -0
- package/dist/src/debug/DebugPanel.d.ts +39 -0
- package/dist/src/debug/index.d.ts +9 -0
- package/dist/src/hooks/use-config-draft.d.ts +46 -0
- package/dist/src/index.d.ts +4 -0
- package/dist/src/renderers/action/action-bar.d.ts +25 -0
- package/dist/src/renderers/action/action-button.d.ts +1 -0
- package/dist/src/types/config-panel.d.ts +92 -0
- package/dist/src/ui/sheet.d.ts +2 -0
- package/dist/src/ui/sidebar.d.ts +4 -0
- package/package.json +17 -17
- package/src/__tests__/__snapshots__/snapshot-critical.test.tsx.snap +3 -3
- package/src/__tests__/action-bar.test.tsx +206 -0
- package/src/__tests__/config-field-renderer.test.tsx +307 -0
- package/src/__tests__/config-panel-renderer.test.tsx +580 -0
- package/src/__tests__/config-primitives.test.tsx +106 -0
- package/src/__tests__/filter-builder.test.tsx +409 -0
- package/src/__tests__/mobile-accessibility.test.tsx +120 -0
- package/src/__tests__/navigation-overlay.test.tsx +97 -0
- package/src/__tests__/use-config-draft.test.tsx +295 -0
- package/src/custom/config-field-renderer.tsx +276 -0
- package/src/custom/config-panel-renderer.tsx +306 -0
- package/src/custom/config-row.tsx +50 -0
- package/src/custom/filter-builder.tsx +76 -25
- package/src/custom/index.ts +5 -0
- package/src/custom/mobile-dialog-content.tsx +67 -0
- package/src/custom/navigation-overlay.tsx +42 -4
- package/src/custom/section-header.tsx +68 -0
- package/src/debug/DebugPanel.tsx +313 -0
- package/src/debug/__tests__/DebugPanel.test.tsx +134 -0
- package/src/debug/index.ts +10 -0
- package/src/hooks/use-config-draft.ts +127 -0
- package/src/index.css +4 -0
- package/src/index.ts +15 -0
- package/src/renderers/action/action-bar.tsx +221 -0
- package/src/renderers/action/action-button.tsx +17 -6
- package/src/renderers/action/index.ts +1 -0
- package/src/renderers/complex/__tests__/data-table-airtable-ux.test.tsx +239 -0
- package/src/renderers/complex/__tests__/data-table.test.ts +16 -0
- package/src/renderers/complex/data-table.tsx +346 -43
- package/src/renderers/data-display/breadcrumb.tsx +3 -2
- package/src/renderers/form/form.tsx +4 -4
- package/src/renderers/navigation/header-bar.tsx +69 -10
- package/src/stories/ConfigPanel.stories.tsx +232 -0
- package/src/types/config-panel.ts +101 -0
- package/src/ui/dialog.tsx +20 -3
- package/src/ui/sheet.tsx +6 -3
- package/src/ui/sidebar.tsx +93 -9
package/src/ui/sidebar.tsx
CHANGED
|
@@ -39,6 +39,11 @@ const SIDEBAR_WIDTH = "16rem"
|
|
|
39
39
|
const SIDEBAR_WIDTH_MOBILE = "18rem"
|
|
40
40
|
const SIDEBAR_WIDTH_ICON = "3rem"
|
|
41
41
|
const SIDEBAR_KEYBOARD_SHORTCUT = "b"
|
|
42
|
+
const SIDEBAR_WIDTH_STORAGE_KEY = "sidebar_width"
|
|
43
|
+
const SIDEBAR_MIN_WIDTH = 200 // px
|
|
44
|
+
const SIDEBAR_MAX_WIDTH = 480 // px
|
|
45
|
+
const SIDEBAR_DEFAULT_WIDTH_PX = 256 // 16rem in px
|
|
46
|
+
const SIDEBAR_CLICK_THRESHOLD_PX = 5
|
|
42
47
|
|
|
43
48
|
type SidebarContextProps = {
|
|
44
49
|
state: "expanded" | "collapsed"
|
|
@@ -48,6 +53,10 @@ type SidebarContextProps = {
|
|
|
48
53
|
setOpenMobile: (open: boolean) => void
|
|
49
54
|
isMobile: boolean
|
|
50
55
|
toggleSidebar: () => void
|
|
56
|
+
/** Current sidebar width in pixels (only used when resizable) */
|
|
57
|
+
sidebarWidth: number | null
|
|
58
|
+
/** Update sidebar width (only used when resizable) */
|
|
59
|
+
setSidebarWidth: (width: number | null) => void
|
|
51
60
|
}
|
|
52
61
|
|
|
53
62
|
const SidebarContext = React.createContext<SidebarContextProps | null>(null)
|
|
@@ -84,6 +93,27 @@ const SidebarProvider = React.forwardRef<
|
|
|
84
93
|
const isMobile = useIsMobile()
|
|
85
94
|
const [openMobile, setOpenMobile] = React.useState(false)
|
|
86
95
|
|
|
96
|
+
// Resizable sidebar width state (persisted to localStorage)
|
|
97
|
+
const [sidebarWidth, setSidebarWidthState] = React.useState<number | null>(() => {
|
|
98
|
+
try {
|
|
99
|
+
const stored = localStorage.getItem(SIDEBAR_WIDTH_STORAGE_KEY)
|
|
100
|
+
if (!stored) return null
|
|
101
|
+
const value = Number(stored)
|
|
102
|
+
if (isNaN(value)) return null
|
|
103
|
+
return Math.max(SIDEBAR_MIN_WIDTH, Math.min(SIDEBAR_MAX_WIDTH, value))
|
|
104
|
+
} catch { return null }
|
|
105
|
+
})
|
|
106
|
+
const setSidebarWidth = React.useCallback((width: number | null) => {
|
|
107
|
+
setSidebarWidthState(width)
|
|
108
|
+
try {
|
|
109
|
+
if (width != null) {
|
|
110
|
+
localStorage.setItem(SIDEBAR_WIDTH_STORAGE_KEY, String(width))
|
|
111
|
+
} else {
|
|
112
|
+
localStorage.removeItem(SIDEBAR_WIDTH_STORAGE_KEY)
|
|
113
|
+
}
|
|
114
|
+
} catch { /* ignore */ }
|
|
115
|
+
}, [])
|
|
116
|
+
|
|
87
117
|
// This is the internal state of the sidebar.
|
|
88
118
|
// We use openProp and setOpenProp for control from outside the component.
|
|
89
119
|
const [_open, _setOpen] = React.useState(defaultOpen)
|
|
@@ -139,8 +169,10 @@ const SidebarProvider = React.forwardRef<
|
|
|
139
169
|
openMobile,
|
|
140
170
|
setOpenMobile,
|
|
141
171
|
toggleSidebar,
|
|
172
|
+
sidebarWidth,
|
|
173
|
+
setSidebarWidth,
|
|
142
174
|
}),
|
|
143
|
-
[state, open, setOpen, isMobile, openMobile, setOpenMobile, toggleSidebar]
|
|
175
|
+
[state, open, setOpen, isMobile, openMobile, setOpenMobile, toggleSidebar, sidebarWidth, setSidebarWidth]
|
|
144
176
|
)
|
|
145
177
|
|
|
146
178
|
return (
|
|
@@ -149,7 +181,7 @@ const SidebarProvider = React.forwardRef<
|
|
|
149
181
|
<div
|
|
150
182
|
style={
|
|
151
183
|
{
|
|
152
|
-
"--sidebar-width": SIDEBAR_WIDTH,
|
|
184
|
+
"--sidebar-width": sidebarWidth ? `${sidebarWidth}px` : SIDEBAR_WIDTH,
|
|
153
185
|
"--sidebar-width-icon": SIDEBAR_WIDTH_ICON,
|
|
154
186
|
...style,
|
|
155
187
|
} as React.CSSProperties
|
|
@@ -307,7 +339,47 @@ const SidebarRail = React.forwardRef<
|
|
|
307
339
|
HTMLButtonElement,
|
|
308
340
|
React.ComponentProps<"button">
|
|
309
341
|
>(({ className, ...props }, ref) => {
|
|
310
|
-
const { toggleSidebar } = useSidebar()
|
|
342
|
+
const { toggleSidebar, setSidebarWidth } = useSidebar()
|
|
343
|
+
const dragging = React.useRef(false)
|
|
344
|
+
const startX = React.useRef(0)
|
|
345
|
+
const startWidth = React.useRef(0)
|
|
346
|
+
|
|
347
|
+
const handlePointerDown = React.useCallback((e: React.PointerEvent) => {
|
|
348
|
+
// Only initiate resize on left mouse button
|
|
349
|
+
if (e.button !== 0) return
|
|
350
|
+
e.preventDefault()
|
|
351
|
+
dragging.current = true
|
|
352
|
+
startX.current = e.clientX
|
|
353
|
+
|
|
354
|
+
// Get the current sidebar width from computed CSS variable
|
|
355
|
+
const wrapper = (e.target as HTMLElement).closest('[style*="--sidebar-width"]') as HTMLElement | null
|
|
356
|
+
startWidth.current = wrapper
|
|
357
|
+
? parseInt(getComputedStyle(wrapper).getPropertyValue('--sidebar-width')) || SIDEBAR_DEFAULT_WIDTH_PX
|
|
358
|
+
: SIDEBAR_DEFAULT_WIDTH_PX
|
|
359
|
+
|
|
360
|
+
const onPointerMove = (ev: PointerEvent) => {
|
|
361
|
+
if (!dragging.current) return
|
|
362
|
+
const delta = ev.clientX - startX.current
|
|
363
|
+
const newWidth = Math.max(SIDEBAR_MIN_WIDTH, Math.min(SIDEBAR_MAX_WIDTH, startWidth.current + delta))
|
|
364
|
+
setSidebarWidth(newWidth)
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
const onPointerUp = () => {
|
|
368
|
+
dragging.current = false
|
|
369
|
+
document.removeEventListener('pointermove', onPointerMove)
|
|
370
|
+
document.removeEventListener('pointerup', onPointerUp)
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
document.addEventListener('pointermove', onPointerMove)
|
|
374
|
+
document.addEventListener('pointerup', onPointerUp)
|
|
375
|
+
}, [setSidebarWidth])
|
|
376
|
+
|
|
377
|
+
const handleClick = React.useCallback((e: React.MouseEvent) => {
|
|
378
|
+
// Only toggle on click (not at end of drag)
|
|
379
|
+
if (Math.abs(e.clientX - startX.current) < SIDEBAR_CLICK_THRESHOLD_PX) {
|
|
380
|
+
toggleSidebar()
|
|
381
|
+
}
|
|
382
|
+
}, [toggleSidebar])
|
|
311
383
|
|
|
312
384
|
return (
|
|
313
385
|
<button
|
|
@@ -315,11 +387,13 @@ const SidebarRail = React.forwardRef<
|
|
|
315
387
|
data-sidebar="rail"
|
|
316
388
|
aria-label="Toggle Sidebar"
|
|
317
389
|
tabIndex={-1}
|
|
318
|
-
|
|
319
|
-
|
|
390
|
+
onPointerDown={handlePointerDown}
|
|
391
|
+
onClick={handleClick}
|
|
392
|
+
onDoubleClick={() => setSidebarWidth(null)}
|
|
393
|
+
title="Drag to resize, click to toggle, double-click to reset"
|
|
320
394
|
className={cn(
|
|
321
395
|
"absolute inset-y-0 z-20 hidden w-4 -translate-x-1/2 transition-all ease-linear after:absolute after:inset-y-0 after:left-1/2 after:w-[2px] hover:after:bg-sidebar-border group-data-[side=left]:-right-4 group-data-[side=right]:left-0 sm:flex",
|
|
322
|
-
"[[data-side=left]_&]:cursor-
|
|
396
|
+
"[[data-side=left]_&]:cursor-col-resize [[data-side=right]_&]:cursor-col-resize",
|
|
323
397
|
"[[data-side=left][data-state=collapsed]_&]:cursor-e-resize [[data-side=right][data-state=collapsed]_&]:cursor-w-resize",
|
|
324
398
|
"group-data-[collapsible=offcanvas]:translate-x-0 group-data-[collapsible=offcanvas]:after:left-full group-data-[collapsible=offcanvas]:hover:bg-sidebar",
|
|
325
399
|
"[[data-side=left][data-collapsible=offcanvas]_&]:-right-2",
|
|
@@ -340,7 +414,7 @@ const SidebarInset = React.forwardRef<
|
|
|
340
414
|
<main
|
|
341
415
|
ref={ref}
|
|
342
416
|
className={cn(
|
|
343
|
-
"relative flex w-full flex-1 flex-col bg-background",
|
|
417
|
+
"relative flex min-w-0 w-full flex-1 flex-col bg-background overflow-hidden",
|
|
344
418
|
"md:peer-data-[variant=inset]:m-2 md:peer-data-[state=collapsed]:peer-data-[variant=inset]:ml-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:shadow",
|
|
345
419
|
className
|
|
346
420
|
)}
|
|
@@ -457,7 +531,7 @@ const SidebarGroupLabel = React.forwardRef<
|
|
|
457
531
|
ref={ref}
|
|
458
532
|
data-sidebar="group-label"
|
|
459
533
|
className={cn(
|
|
460
|
-
"flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium text-sidebar-foreground/70 outline-none ring-sidebar-ring transition-[margin,opacity] duration-200 ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0",
|
|
534
|
+
"flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium text-sidebar-foreground/70 outline-none ring-sidebar-ring transition-[margin,opacity] duration-200 ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0 border-t border-border/30 pt-3 mt-2 first:border-t-0 first:pt-0 first:mt-0",
|
|
461
535
|
"group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0",
|
|
462
536
|
className
|
|
463
537
|
)}
|
|
@@ -530,7 +604,17 @@ const SidebarMenuItem = React.forwardRef<
|
|
|
530
604
|
SidebarMenuItem.displayName = "SidebarMenuItem"
|
|
531
605
|
|
|
532
606
|
const sidebarMenuButtonVariants = cva(
|
|
533
|
-
|
|
607
|
+
[
|
|
608
|
+
"peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-none ring-sidebar-ring transition-[width,height,padding,color,background-color] duration-150",
|
|
609
|
+
"hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground",
|
|
610
|
+
"disabled:pointer-events-none disabled:opacity-50 group-has-[[data-sidebar=menu-action]]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50",
|
|
611
|
+
"data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground",
|
|
612
|
+
"data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground",
|
|
613
|
+
"group-data-[collapsible=icon]:!size-8 group-data-[collapsible=icon]:!p-2 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0",
|
|
614
|
+
// Active indicator bar (3px left accent)
|
|
615
|
+
"relative data-[active=true]:before:absolute data-[active=true]:before:left-0 data-[active=true]:before:top-1/2 data-[active=true]:before:-translate-y-1/2",
|
|
616
|
+
"data-[active=true]:before:h-4 data-[active=true]:before:w-[3px] data-[active=true]:before:rounded-full data-[active=true]:before:bg-primary",
|
|
617
|
+
].join(" "),
|
|
534
618
|
{
|
|
535
619
|
variants: {
|
|
536
620
|
variant: {
|