@invopop/popui 0.1.97 → 0.1.99

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 (87) hide show
  1. package/dist/CounterWidget.svelte +18 -1
  2. package/dist/GlobalSearch.svelte +1 -1
  3. package/dist/MenuItem.svelte +1 -1
  4. package/dist/index.d.ts +36 -1
  5. package/dist/index.js +74 -0
  6. package/dist/separator/index.d.ts +2 -0
  7. package/dist/separator/index.js +4 -0
  8. package/dist/separator/separator.svelte +21 -0
  9. package/dist/separator/separator.svelte.d.ts +4 -0
  10. package/dist/sheet/index.d.ts +11 -0
  11. package/dist/sheet/index.js +13 -0
  12. package/dist/sheet/sheet-close.svelte +7 -0
  13. package/dist/sheet/sheet-close.svelte.d.ts +4 -0
  14. package/dist/sheet/sheet-content.svelte +65 -0
  15. package/dist/sheet/sheet-content.svelte.d.ts +15 -0
  16. package/dist/sheet/sheet-description.svelte +17 -0
  17. package/dist/sheet/sheet-description.svelte.d.ts +4 -0
  18. package/dist/sheet/sheet-footer.svelte +20 -0
  19. package/dist/sheet/sheet-footer.svelte.d.ts +5 -0
  20. package/dist/sheet/sheet-header.svelte +20 -0
  21. package/dist/sheet/sheet-header.svelte.d.ts +5 -0
  22. package/dist/sheet/sheet-overlay.svelte +20 -0
  23. package/dist/sheet/sheet-overlay.svelte.d.ts +4 -0
  24. package/dist/sheet/sheet-portal.svelte +7 -0
  25. package/dist/sheet/sheet-portal.svelte.d.ts +3 -0
  26. package/dist/sheet/sheet-title.svelte +17 -0
  27. package/dist/sheet/sheet-title.svelte.d.ts +4 -0
  28. package/dist/sheet/sheet-trigger.svelte +7 -0
  29. package/dist/sheet/sheet-trigger.svelte.d.ts +4 -0
  30. package/dist/sheet/sheet.svelte +7 -0
  31. package/dist/sheet/sheet.svelte.d.ts +3 -0
  32. package/dist/sidebar/constants.d.ts +10 -0
  33. package/dist/sidebar/constants.js +10 -0
  34. package/dist/sidebar/context.svelte.d.ts +23 -0
  35. package/dist/sidebar/context.svelte.js +58 -0
  36. package/dist/sidebar/index.d.ts +25 -0
  37. package/dist/sidebar/index.js +27 -0
  38. package/dist/sidebar/is-mobile.svelte.d.ts +4 -0
  39. package/dist/sidebar/is-mobile.svelte.js +7 -0
  40. package/dist/sidebar/sidebar-content.svelte +25 -0
  41. package/dist/sidebar/sidebar-content.svelte.d.ts +5 -0
  42. package/dist/sidebar/sidebar-footer.svelte +21 -0
  43. package/dist/sidebar/sidebar-footer.svelte.d.ts +5 -0
  44. package/dist/sidebar/sidebar-group-action.svelte +33 -0
  45. package/dist/sidebar/sidebar-group-action.svelte.d.ts +11 -0
  46. package/dist/sidebar/sidebar-group-content.svelte +21 -0
  47. package/dist/sidebar/sidebar-group-content.svelte.d.ts +5 -0
  48. package/dist/sidebar/sidebar-group-label.svelte +33 -0
  49. package/dist/sidebar/sidebar-group-label.svelte.d.ts +11 -0
  50. package/dist/sidebar/sidebar-group.svelte +21 -0
  51. package/dist/sidebar/sidebar-group.svelte.d.ts +5 -0
  52. package/dist/sidebar/sidebar-header.svelte +21 -0
  53. package/dist/sidebar/sidebar-header.svelte.d.ts +5 -0
  54. package/dist/sidebar/sidebar-input.svelte +23 -0
  55. package/dist/sidebar/sidebar-input.svelte.d.ts +5 -0
  56. package/dist/sidebar/sidebar-inset.svelte +23 -0
  57. package/dist/sidebar/sidebar-inset.svelte.d.ts +5 -0
  58. package/dist/sidebar/sidebar-menu-action.svelte +37 -0
  59. package/dist/sidebar/sidebar-menu-action.svelte.d.ts +12 -0
  60. package/dist/sidebar/sidebar-menu-badge.svelte +24 -0
  61. package/dist/sidebar/sidebar-menu-badge.svelte.d.ts +5 -0
  62. package/dist/sidebar/sidebar-menu-button.svelte +102 -0
  63. package/dist/sidebar/sidebar-menu-button.svelte.d.ts +51 -0
  64. package/dist/sidebar/sidebar-menu-item.svelte +21 -0
  65. package/dist/sidebar/sidebar-menu-item.svelte.d.ts +5 -0
  66. package/dist/sidebar/sidebar-menu-skeleton.svelte +35 -0
  67. package/dist/sidebar/sidebar-menu-skeleton.svelte.d.ts +8 -0
  68. package/dist/sidebar/sidebar-menu-sub-button.svelte +39 -0
  69. package/dist/sidebar/sidebar-menu-sub-button.svelte.d.ts +13 -0
  70. package/dist/sidebar/sidebar-menu-sub-item.svelte +21 -0
  71. package/dist/sidebar/sidebar-menu-sub-item.svelte.d.ts +5 -0
  72. package/dist/sidebar/sidebar-menu-sub.svelte +24 -0
  73. package/dist/sidebar/sidebar-menu-sub.svelte.d.ts +5 -0
  74. package/dist/sidebar/sidebar-menu.svelte +21 -0
  75. package/dist/sidebar/sidebar-menu.svelte.d.ts +5 -0
  76. package/dist/sidebar/sidebar-provider.svelte +47 -0
  77. package/dist/sidebar/sidebar-provider.svelte.d.ts +9 -0
  78. package/dist/sidebar/sidebar-rail.svelte +176 -0
  79. package/dist/sidebar/sidebar-rail.svelte.d.ts +5 -0
  80. package/dist/sidebar/sidebar-separator.svelte +19 -0
  81. package/dist/sidebar/sidebar-separator.svelte.d.ts +13 -0
  82. package/dist/sidebar/sidebar-trigger.svelte +35 -0
  83. package/dist/sidebar/sidebar-trigger.svelte.d.ts +5 -0
  84. package/dist/sidebar/sidebar.svelte +106 -0
  85. package/dist/sidebar/sidebar.svelte.d.ts +10 -0
  86. package/dist/tailwind.theme.css +20 -0
  87. package/package.json +2 -1
@@ -0,0 +1,35 @@
1
+ <script lang="ts">
2
+ import type { HTMLAttributes } from 'svelte/elements'
3
+ import { cn, type WithElementRef } from '../utils.js'
4
+ import { Skeleton } from '../skeleton/index.js'
5
+
6
+ let {
7
+ ref = $bindable(null),
8
+ class: className,
9
+ showIcon = false,
10
+ children,
11
+ ...restProps
12
+ }: WithElementRef<HTMLAttributes<HTMLElement>> & {
13
+ showIcon?: boolean
14
+ } = $props()
15
+
16
+ const width = `${Math.floor(Math.random() * 40) + 50}%`
17
+ </script>
18
+
19
+ <div
20
+ bind:this={ref}
21
+ data-slot="sidebar-menu-skeleton"
22
+ data-sidebar="menu-skeleton"
23
+ class={cn('flex h-8 items-center gap-2 rounded-md px-2', className)}
24
+ {...restProps}
25
+ >
26
+ {#if showIcon}
27
+ <Skeleton class="size-4 rounded-md" data-sidebar="menu-skeleton-icon" />
28
+ {/if}
29
+ <Skeleton
30
+ class="h-4 max-w-(--skeleton-width) flex-1"
31
+ data-sidebar="menu-skeleton-text"
32
+ style="--skeleton-width: {width};"
33
+ />
34
+ {@render children?.()}
35
+ </div>
@@ -0,0 +1,8 @@
1
+ import type { HTMLAttributes } from 'svelte/elements';
2
+ import { type WithElementRef } from '../utils.js';
3
+ type $$ComponentProps = WithElementRef<HTMLAttributes<HTMLElement>> & {
4
+ showIcon?: boolean;
5
+ };
6
+ declare const SidebarMenuSkeleton: import("svelte").Component<$$ComponentProps, {}, "ref">;
7
+ type SidebarMenuSkeleton = ReturnType<typeof SidebarMenuSkeleton>;
8
+ export default SidebarMenuSkeleton;
@@ -0,0 +1,39 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte'
3
+ import type { HTMLAnchorAttributes } from 'svelte/elements'
4
+ import { cn, type WithElementRef } from '../utils.js'
5
+
6
+ let {
7
+ ref = $bindable(null),
8
+ children,
9
+ child,
10
+ class: className,
11
+ size = 'md',
12
+ isActive = false,
13
+ ...restProps
14
+ }: WithElementRef<HTMLAnchorAttributes> & {
15
+ child?: Snippet<[{ props: Record<string, unknown> }]>
16
+ size?: 'sm' | 'md'
17
+ isActive?: boolean
18
+ } = $props()
19
+
20
+ const mergedProps = $derived({
21
+ class: cn(
22
+ 'text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:bg-sidebar-accent active:text-sidebar-accent-foreground [&>svg]:text-sidebar-accent-foreground data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground flex h-7 min-w-0 -translate-x-px items-center gap-2 overflow-hidden rounded-md px-2 outline-hidden focus-visible:ring-2 group-data-[collapsible=icon]:hidden disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0 data-[size=md]:text-sm data-[size=sm]:text-xs',
23
+ className
24
+ ),
25
+ 'data-slot': 'sidebar-menu-sub-button',
26
+ 'data-sidebar': 'menu-sub-button',
27
+ 'data-size': size,
28
+ 'data-active': isActive,
29
+ ...restProps
30
+ })
31
+ </script>
32
+
33
+ {#if child}
34
+ {@render child({ props: mergedProps })}
35
+ {:else}
36
+ <a bind:this={ref} {...mergedProps}>
37
+ {@render children?.()}
38
+ </a>
39
+ {/if}
@@ -0,0 +1,13 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { HTMLAnchorAttributes } from 'svelte/elements';
3
+ import { type WithElementRef } from '../utils.js';
4
+ type $$ComponentProps = WithElementRef<HTMLAnchorAttributes> & {
5
+ child?: Snippet<[{
6
+ props: Record<string, unknown>;
7
+ }]>;
8
+ size?: 'sm' | 'md';
9
+ isActive?: boolean;
10
+ };
11
+ declare const SidebarMenuSubButton: import("svelte").Component<$$ComponentProps, {}, "ref">;
12
+ type SidebarMenuSubButton = ReturnType<typeof SidebarMenuSubButton>;
13
+ export default SidebarMenuSubButton;
@@ -0,0 +1,21 @@
1
+ <script lang="ts">
2
+ import type { HTMLAttributes } from 'svelte/elements'
3
+ import { cn, type WithElementRef } from '../utils.js'
4
+
5
+ let {
6
+ ref = $bindable(null),
7
+ children,
8
+ class: className,
9
+ ...restProps
10
+ }: WithElementRef<HTMLAttributes<HTMLLIElement>> = $props()
11
+ </script>
12
+
13
+ <li
14
+ bind:this={ref}
15
+ data-slot="sidebar-menu-sub-item"
16
+ data-sidebar="menu-sub-item"
17
+ class={cn('group/menu-sub-item relative', className)}
18
+ {...restProps}
19
+ >
20
+ {@render children?.()}
21
+ </li>
@@ -0,0 +1,5 @@
1
+ import type { HTMLAttributes } from 'svelte/elements';
2
+ import { type WithElementRef } from '../utils.js';
3
+ declare const SidebarMenuSubItem: import("svelte").Component<WithElementRef<HTMLAttributes<HTMLLIElement>>, {}, "ref">;
4
+ type SidebarMenuSubItem = ReturnType<typeof SidebarMenuSubItem>;
5
+ export default SidebarMenuSubItem;
@@ -0,0 +1,24 @@
1
+ <script lang="ts">
2
+ import type { HTMLAttributes } from 'svelte/elements'
3
+ import { cn, type WithElementRef } from '../utils.js'
4
+
5
+ let {
6
+ ref = $bindable(null),
7
+ class: className,
8
+ children,
9
+ ...restProps
10
+ }: WithElementRef<HTMLAttributes<HTMLUListElement>> = $props()
11
+ </script>
12
+
13
+ <ul
14
+ bind:this={ref}
15
+ data-slot="sidebar-menu-sub"
16
+ data-sidebar="menu-sub"
17
+ class={cn(
18
+ 'border-sidebar-border mx-3.5 flex min-w-0 translate-x-px flex-col gap-1 border-l px-2.5 py-0.5 group-data-[collapsible=icon]:hidden',
19
+ className
20
+ )}
21
+ {...restProps}
22
+ >
23
+ {@render children?.()}
24
+ </ul>
@@ -0,0 +1,5 @@
1
+ import type { HTMLAttributes } from 'svelte/elements';
2
+ import { type WithElementRef } from '../utils.js';
3
+ declare const SidebarMenuSub: import("svelte").Component<WithElementRef<HTMLAttributes<HTMLUListElement>>, {}, "ref">;
4
+ type SidebarMenuSub = ReturnType<typeof SidebarMenuSub>;
5
+ export default SidebarMenuSub;
@@ -0,0 +1,21 @@
1
+ <script lang="ts">
2
+ import type { HTMLAttributes } from 'svelte/elements'
3
+ import { cn, type WithElementRef } from '../utils.js'
4
+
5
+ let {
6
+ ref = $bindable(null),
7
+ class: className,
8
+ children,
9
+ ...restProps
10
+ }: WithElementRef<HTMLAttributes<HTMLUListElement>, HTMLUListElement> = $props()
11
+ </script>
12
+
13
+ <ul
14
+ bind:this={ref}
15
+ data-slot="sidebar-menu"
16
+ data-sidebar="menu"
17
+ class={cn('flex w-full min-w-0 flex-col gap-1', className)}
18
+ {...restProps}
19
+ >
20
+ {@render children?.()}
21
+ </ul>
@@ -0,0 +1,5 @@
1
+ import type { HTMLAttributes } from 'svelte/elements';
2
+ import { type WithElementRef } from '../utils.js';
3
+ declare const SidebarMenu: import("svelte").Component<WithElementRef<HTMLAttributes<HTMLUListElement>, HTMLUListElement>, {}, "ref">;
4
+ type SidebarMenu = ReturnType<typeof SidebarMenu>;
5
+ export default SidebarMenu;
@@ -0,0 +1,47 @@
1
+ <script lang="ts">
2
+ import type { HTMLAttributes } from 'svelte/elements'
3
+ import * as Tooltip from '../tooltip/index.js'
4
+ import { cn, type WithElementRef } from '../utils.js'
5
+ import { SIDEBAR_COOKIE_MAX_AGE, SIDEBAR_COOKIE_NAME, SIDEBAR_WIDTH_ICON } from './constants.js'
6
+ import { setSidebar } from './context.svelte.js'
7
+
8
+ let {
9
+ ref = $bindable(null),
10
+ open = $bindable(true),
11
+ onOpenChange = () => {},
12
+ class: className,
13
+ style,
14
+ children,
15
+ ...restProps
16
+ }: WithElementRef<HTMLAttributes<HTMLDivElement>> & {
17
+ open?: boolean
18
+ onOpenChange?: (open: boolean) => void
19
+ } = $props()
20
+
21
+ const sidebar = setSidebar({
22
+ open: () => open,
23
+ setOpen: (value: boolean) => {
24
+ open = value
25
+ onOpenChange(value)
26
+ document.cookie = `${SIDEBAR_COOKIE_NAME}=${open}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`
27
+ }
28
+ })
29
+ </script>
30
+
31
+ <svelte:window onkeydown={sidebar.handleShortcutKeydown} />
32
+
33
+ <Tooltip.Provider delayDuration={0}>
34
+ <div
35
+ data-slot="sidebar-wrapper"
36
+ style="--sidebar-width: {sidebar.width}; --sidebar-width-icon: {SIDEBAR_WIDTH_ICON}; {style ??
37
+ ''}"
38
+ class={cn(
39
+ 'group/sidebar-wrapper flex min-h-svh w-full has-data-[variant=inset]:bg-sidebar',
40
+ className
41
+ )}
42
+ bind:this={ref}
43
+ {...restProps}
44
+ >
45
+ {@render children?.()}
46
+ </div>
47
+ </Tooltip.Provider>
@@ -0,0 +1,9 @@
1
+ import type { HTMLAttributes } from 'svelte/elements';
2
+ import { type WithElementRef } from '../utils.js';
3
+ type $$ComponentProps = WithElementRef<HTMLAttributes<HTMLDivElement>> & {
4
+ open?: boolean;
5
+ onOpenChange?: (open: boolean) => void;
6
+ };
7
+ declare const SidebarProvider: import("svelte").Component<$$ComponentProps, {}, "open" | "ref">;
8
+ type SidebarProvider = ReturnType<typeof SidebarProvider>;
9
+ export default SidebarProvider;
@@ -0,0 +1,176 @@
1
+ <script lang="ts">
2
+ import type { HTMLAttributes } from 'svelte/elements'
3
+ import ShortcutWrapper from '../ShortcutWrapper.svelte'
4
+ import { cn, type WithElementRef } from '../utils.js'
5
+ import { SIDEBAR_DRAG_THRESHOLD_PX } from './constants.js'
6
+ import { useSidebar } from './context.svelte.js'
7
+
8
+ function portal(node: HTMLElement) {
9
+ const target = typeof document !== 'undefined' ? document.body : null
10
+ if (target) target.appendChild(node)
11
+ return {
12
+ destroy() {
13
+ if (node.parentNode) node.parentNode.removeChild(node)
14
+ }
15
+ }
16
+ }
17
+
18
+ let {
19
+ ref = $bindable(null),
20
+ class: className,
21
+ children,
22
+ ...restProps
23
+ }: WithElementRef<HTMLAttributes<HTMLButtonElement>, HTMLButtonElement> = $props()
24
+
25
+ const sidebar = useSidebar()
26
+
27
+ let dragStartX = 0
28
+ let dragStartWidthPx = 0
29
+ let dragMoved = false
30
+ let dragDirection: 1 | -1 = 1
31
+
32
+ let isHovering = $state(false)
33
+ let isDragging = $state(false)
34
+ let tooltipVisible = $derived(isHovering && !isDragging)
35
+ let tooltipX = $state(0)
36
+ let tooltipY = $state(0)
37
+ let tooltipWidth = $state(0)
38
+ let tooltipHeight = $state(0)
39
+
40
+ const TOOLTIP_CURSOR_OFFSET = 12
41
+ const TOOLTIP_VIEWPORT_PADDING = 8
42
+
43
+ let tooltipLeft = $derived.by(() => {
44
+ if (tooltipWidth === 0 || typeof window === 'undefined') return tooltipX
45
+ const desired = tooltipX - tooltipWidth / 2
46
+ const minX = TOOLTIP_VIEWPORT_PADDING
47
+ const maxX = window.innerWidth - tooltipWidth - TOOLTIP_VIEWPORT_PADDING
48
+ return Math.max(minX, Math.min(maxX, desired))
49
+ })
50
+
51
+ let tooltipTop = $derived.by(() => {
52
+ const desired = tooltipY + TOOLTIP_CURSOR_OFFSET
53
+ if (tooltipHeight === 0 || typeof window === 'undefined') return desired
54
+ const maxY = window.innerHeight - tooltipHeight - TOOLTIP_VIEWPORT_PADDING
55
+ return Math.min(maxY, desired)
56
+ })
57
+
58
+ function updateTooltipPosition(e: PointerEvent) {
59
+ tooltipX = e.clientX
60
+ tooltipY = e.clientY
61
+ }
62
+
63
+ function onPointerEnter(e: PointerEvent) {
64
+ const sidebarRoot = (e.currentTarget as HTMLElement).closest('[data-slot="sidebar"]')
65
+ dragDirection = sidebarRoot?.getAttribute('data-side') === 'right' ? -1 : 1
66
+ updateTooltipPosition(e)
67
+ isHovering = true
68
+ }
69
+
70
+ function onPointerLeave() {
71
+ isHovering = false
72
+ }
73
+
74
+ function onPointerDown(e: PointerEvent) {
75
+ if (sidebar.state !== 'expanded' || sidebar.isMobile) return
76
+ const button = e.currentTarget as HTMLButtonElement
77
+ const sidebarRoot = button.closest('[data-slot="sidebar"]')
78
+ dragDirection = sidebarRoot?.getAttribute('data-side') === 'right' ? -1 : 1
79
+
80
+ const container = sidebarRoot?.querySelector('[data-slot="sidebar-container"]')
81
+ dragStartWidthPx = container instanceof HTMLElement ? container.offsetWidth : 256
82
+ dragStartX = e.clientX
83
+ dragMoved = false
84
+ button.setPointerCapture(e.pointerId)
85
+ document.body.style.cursor = 'col-resize'
86
+ document.body.style.userSelect = 'none'
87
+ }
88
+
89
+ function onPointerMove(e: PointerEvent) {
90
+ if (tooltipVisible) updateTooltipPosition(e)
91
+ const button = e.currentTarget as HTMLButtonElement
92
+ if (!button.hasPointerCapture(e.pointerId)) return
93
+ const delta = (e.clientX - dragStartX) * dragDirection
94
+ if (Math.abs(delta) > SIDEBAR_DRAG_THRESHOLD_PX) {
95
+ dragMoved = true
96
+ isDragging = true
97
+ }
98
+ if (dragMoved) sidebar.setWidth(dragStartWidthPx + delta)
99
+ }
100
+
101
+ function onPointerUp(e: PointerEvent) {
102
+ const button = e.currentTarget as HTMLButtonElement
103
+ if (button.hasPointerCapture(e.pointerId)) button.releasePointerCapture(e.pointerId)
104
+ document.body.style.cursor = ''
105
+ document.body.style.userSelect = ''
106
+ isDragging = false
107
+ }
108
+
109
+ function onClick(e: MouseEvent) {
110
+ if (dragMoved) {
111
+ e.preventDefault()
112
+ e.stopPropagation()
113
+ dragMoved = false
114
+ return
115
+ }
116
+ sidebar.toggle()
117
+ }
118
+ </script>
119
+
120
+ <button
121
+ bind:this={ref}
122
+ data-sidebar="rail"
123
+ data-slot="sidebar-rail"
124
+ aria-label="Toggle Sidebar"
125
+ tabindex={-1}
126
+ type="button"
127
+ onpointerenter={onPointerEnter}
128
+ onpointerleave={onPointerLeave}
129
+ onpointerdown={onPointerDown}
130
+ onpointermove={onPointerMove}
131
+ onpointerup={onPointerUp}
132
+ onpointercancel={onPointerUp}
133
+ onclick={onClick}
134
+ class={cn(
135
+ 'absolute inset-y-0 z-20 hidden w-4 -translate-x-1/2 transition-all ease-linear group-data-[side=left]:-right-4 group-data-[side=right]:left-0 sm:flex',
136
+ 'after:absolute after:inset-y-0 after:left-1/2 after:w-[2px]',
137
+ 'hover:after:bg-sidebar-border',
138
+ 'group-data-[side=left]:cursor-w-resize group-data-[side=right]:cursor-e-resize',
139
+ '[[data-side=left][data-state=collapsed]_&]:cursor-e-resize [[data-side=right][data-state=collapsed]_&]:cursor-w-resize',
140
+ 'hover:group-data-[collapsible=offcanvas]:bg-sidebar group-data-[collapsible=offcanvas]:translate-x-0 group-data-[collapsible=offcanvas]:after:left-full',
141
+ '[[data-side=left][data-collapsible=offcanvas]_&]:-right-2',
142
+ '[[data-side=right][data-collapsible=offcanvas]_&]:-left-2',
143
+ className
144
+ )}
145
+ {...restProps}
146
+ >
147
+ {@render children?.()}
148
+ </button>
149
+
150
+ {#if tooltipVisible}
151
+ <div
152
+ use:portal
153
+ role="tooltip"
154
+ bind:offsetWidth={tooltipWidth}
155
+ bind:offsetHeight={tooltipHeight}
156
+ class="fixed z-[1002] pointer-events-none rounded-md border border-border-inverse bg-background-default-negative px-3 py-2 text-sm font-medium text-foreground-inverse leading-5 tracking-tight shadow-md"
157
+ style="left: {tooltipLeft}px; top: {tooltipTop}px; visibility: {tooltipWidth > 0
158
+ ? 'visible'
159
+ : 'hidden'};"
160
+ >
161
+ <div class="flex flex-col gap-1.5">
162
+ {#if sidebar.state === 'expanded'}
163
+ <div>Drag to resize</div>
164
+ {/if}
165
+ <div class="flex items-center justify-between gap-3">
166
+ <span>
167
+ {sidebar.state === 'expanded' ? 'Click to collapse' : 'Click to expand'}
168
+ </span>
169
+ <div class="flex items-center gap-1">
170
+ <ShortcutWrapper size="md" theme="navigation">⌘</ShortcutWrapper>
171
+ <ShortcutWrapper size="md" theme="navigation">.</ShortcutWrapper>
172
+ </div>
173
+ </div>
174
+ </div>
175
+ </div>
176
+ {/if}
@@ -0,0 +1,5 @@
1
+ import type { HTMLAttributes } from 'svelte/elements';
2
+ import { type WithElementRef } from '../utils.js';
3
+ declare const SidebarRail: import("svelte").Component<WithElementRef<HTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, {}, "ref">;
4
+ type SidebarRail = ReturnType<typeof SidebarRail>;
5
+ export default SidebarRail;
@@ -0,0 +1,19 @@
1
+ <script lang="ts">
2
+ import type { ComponentProps } from 'svelte'
3
+ import { Separator } from '../separator/index.js'
4
+ import { cn } from '../utils.js'
5
+
6
+ let {
7
+ ref = $bindable(null),
8
+ class: className,
9
+ ...restProps
10
+ }: ComponentProps<typeof Separator> = $props()
11
+ </script>
12
+
13
+ <Separator
14
+ bind:ref
15
+ data-slot="sidebar-separator"
16
+ data-sidebar="separator"
17
+ class={cn('bg-sidebar-border mx-2 w-auto', className)}
18
+ {...restProps}
19
+ />
@@ -0,0 +1,13 @@
1
+ declare const SidebarSeparator: import("svelte").Component<Omit<{
2
+ orientation?: import("bits-ui").Orientation;
3
+ decorative?: boolean;
4
+ }, "children" | "child"> & {
5
+ child?: import("svelte").Snippet<[{
6
+ props: Record<string, unknown>;
7
+ }]> | undefined;
8
+ children?: import("svelte").Snippet<[]> | undefined;
9
+ style?: import("bits-ui").StyleProperties | string | null | undefined;
10
+ ref?: HTMLElement | null | undefined;
11
+ } & import("bits-ui").Without<import("bits-ui").BitsPrimitiveDivAttributes, import("bits-ui").SeparatorRootPropsWithoutHTML>, {}, "ref">;
12
+ type SidebarSeparator = ReturnType<typeof SidebarSeparator>;
13
+ export default SidebarSeparator;
@@ -0,0 +1,35 @@
1
+ <script lang="ts">
2
+ import type { HTMLButtonAttributes } from 'svelte/elements'
3
+ import { SidebarShow } from '@invopop/ui-icons'
4
+ import { Icon } from '@steeze-ui/svelte-icon'
5
+ import { cn, type WithElementRef } from '../utils.js'
6
+ import { useSidebar } from './context.svelte.js'
7
+
8
+ let {
9
+ ref = $bindable(null),
10
+ class: className,
11
+ onclick,
12
+ ...restProps
13
+ }: WithElementRef<HTMLButtonAttributes, HTMLButtonElement> = $props()
14
+
15
+ const sidebar = useSidebar()
16
+ </script>
17
+
18
+ <button
19
+ bind:this={ref}
20
+ data-sidebar="trigger"
21
+ data-slot="sidebar-trigger"
22
+ type="button"
23
+ class={cn(
24
+ 'inline-flex size-7 items-center justify-center rounded-md text-foreground transition-colors hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-sidebar-ring disabled:pointer-events-none disabled:opacity-50',
25
+ className
26
+ )}
27
+ onclick={(e) => {
28
+ onclick?.(e)
29
+ sidebar.toggle()
30
+ }}
31
+ {...restProps}
32
+ >
33
+ <Icon src={SidebarShow} class="size-4" />
34
+ <span class="sr-only">Toggle Sidebar</span>
35
+ </button>
@@ -0,0 +1,5 @@
1
+ import type { HTMLButtonAttributes } from 'svelte/elements';
2
+ import { type WithElementRef } from '../utils.js';
3
+ declare const SidebarTrigger: import("svelte").Component<WithElementRef<HTMLButtonAttributes, HTMLButtonElement>, {}, "ref">;
4
+ type SidebarTrigger = ReturnType<typeof SidebarTrigger>;
5
+ export default SidebarTrigger;
@@ -0,0 +1,106 @@
1
+ <script lang="ts">
2
+ import type { HTMLAttributes } from 'svelte/elements'
3
+ import * as Sheet from '../sheet/index.js'
4
+ import { cn, type WithElementRef } from '../utils.js'
5
+ import { SIDEBAR_WIDTH_MOBILE } from './constants.js'
6
+ import { useSidebar } from './context.svelte.js'
7
+
8
+ let {
9
+ ref = $bindable(null),
10
+ side = 'left',
11
+ variant = 'sidebar',
12
+ collapsible = 'offcanvas',
13
+ class: className,
14
+ children,
15
+ ...restProps
16
+ }: WithElementRef<HTMLAttributes<HTMLDivElement>> & {
17
+ side?: 'left' | 'right'
18
+ variant?: 'sidebar' | 'floating' | 'inset'
19
+ collapsible?: 'offcanvas' | 'icon' | 'none'
20
+ } = $props()
21
+
22
+ const sidebar = useSidebar()
23
+ </script>
24
+
25
+ {#if collapsible === 'none'}
26
+ <div
27
+ class={cn(
28
+ 'bg-sidebar text-sidebar-foreground flex h-full w-(--sidebar-width) flex-col',
29
+ className
30
+ )}
31
+ bind:this={ref}
32
+ {...restProps}
33
+ >
34
+ {@render children?.()}
35
+ </div>
36
+ {:else if sidebar.isMobile}
37
+ <Sheet.Root
38
+ bind:open={() => sidebar.openMobile, (v) => sidebar.setOpenMobile(v)}
39
+ {...restProps}
40
+ >
41
+ <Sheet.Content
42
+ data-sidebar="sidebar"
43
+ data-slot="sidebar"
44
+ data-mobile="true"
45
+ class={cn(
46
+ 'bg-sidebar text-sidebar-foreground w-(--sidebar-width) p-0 [&>button]:hidden',
47
+ className
48
+ )}
49
+ style="--sidebar-width: {SIDEBAR_WIDTH_MOBILE};"
50
+ {side}
51
+ showCloseButton={false}
52
+ >
53
+ <Sheet.Header class="sr-only">
54
+ <Sheet.Title>Sidebar</Sheet.Title>
55
+ <Sheet.Description>Displays the mobile sidebar.</Sheet.Description>
56
+ </Sheet.Header>
57
+ <div class="flex h-full w-full flex-col">
58
+ {@render children?.()}
59
+ </div>
60
+ </Sheet.Content>
61
+ </Sheet.Root>
62
+ {:else}
63
+ <div
64
+ bind:this={ref}
65
+ class="text-sidebar-foreground group peer hidden md:block"
66
+ data-state={sidebar.state}
67
+ data-collapsible={sidebar.state === 'collapsed' ? collapsible : ''}
68
+ data-variant={variant}
69
+ data-side={side}
70
+ data-slot="sidebar"
71
+ >
72
+ <div
73
+ data-slot="sidebar-gap"
74
+ class={cn(
75
+ 'relative w-(--sidebar-width) bg-transparent transition-[width] duration-200 ease-linear',
76
+ 'group-data-[collapsible=offcanvas]:w-0',
77
+ 'group-data-[side=right]:rotate-180',
78
+ variant === 'floating' || variant === 'inset'
79
+ ? 'group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+1rem)]'
80
+ : 'group-data-[collapsible=icon]:w-(--sidebar-width-icon)'
81
+ )}
82
+ ></div>
83
+ <div
84
+ data-slot="sidebar-container"
85
+ class={cn(
86
+ 'fixed inset-y-0 z-10 hidden h-svh w-(--sidebar-width) transition-[left,right,width] duration-200 ease-linear md:flex',
87
+ side === 'left'
88
+ ? 'left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]'
89
+ : 'right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]',
90
+ variant === 'floating' || variant === 'inset'
91
+ ? 'p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+1rem+2px)]'
92
+ : 'group-data-[collapsible=icon]:w-(--sidebar-width-icon) group-data-[side=left]:border-r group-data-[side=right]:border-l',
93
+ className
94
+ )}
95
+ {...restProps}
96
+ >
97
+ <div
98
+ data-sidebar="sidebar"
99
+ data-slot="sidebar-inner"
100
+ class="flex size-full flex-col bg-sidebar group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:shadow-sm group-data-[variant=floating]:ring-1 group-data-[variant=floating]:ring-sidebar-border"
101
+ >
102
+ {@render children?.()}
103
+ </div>
104
+ </div>
105
+ </div>
106
+ {/if}
@@ -0,0 +1,10 @@
1
+ import type { HTMLAttributes } from 'svelte/elements';
2
+ import { type WithElementRef } from '../utils.js';
3
+ type $$ComponentProps = WithElementRef<HTMLAttributes<HTMLDivElement>> & {
4
+ side?: 'left' | 'right';
5
+ variant?: 'sidebar' | 'floating' | 'inset';
6
+ collapsible?: 'offcanvas' | 'icon' | 'none';
7
+ };
8
+ declare const Sidebar: import("svelte").Component<$$ComponentProps, {}, "ref">;
9
+ type Sidebar = ReturnType<typeof Sidebar>;
10
+ export default Sidebar;
@@ -837,6 +837,26 @@
837
837
  --text-xs: 10px;
838
838
  --text-xs--line-height: 12px;
839
839
  --text-xs--letter-spacing: var(--tracking-wide);
840
+
841
+ /* ============================================
842
+ SHADCN-COMPATIBLE TOKEN ALIASES (Sheet, Separator, Sidebar)
843
+ ============================================ */
844
+
845
+ /* Generic aliases used by Sheet and Sidebar.Input */
846
+ --color-foreground-secondary: var(--color-foreground-default-secondary);
847
+ --color-input: var(--color-border-default-default);
848
+ --color-ring: var(--color-foreground-default-default);
849
+
850
+ /* Sidebar — defaults to the brand-bold (inverse/dark) surface so popui's
851
+ Sidebar matches the existing dark chrome out of the box. */
852
+ --color-sidebar: var(--color-background-default-bold);
853
+ --color-sidebar-foreground: var(--color-foreground-inverse-default);
854
+ --color-sidebar-primary: var(--color-foreground-inverse-default);
855
+ --color-sidebar-primary-foreground: var(--color-background-default-bold);
856
+ --color-sidebar-accent: var(--color-background-selected-inverse);
857
+ --color-sidebar-accent-foreground: var(--color-foreground-inverse-default);
858
+ --color-sidebar-border: var(--color-border-inverse-default);
859
+ --color-sidebar-ring: var(--color-border-inverse-secondary);
840
860
  }
841
861
 
842
862
  @layer theme {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@invopop/popui",
3
3
  "license": "MIT",
4
- "version": "0.1.97",
4
+ "version": "0.1.99",
5
5
  "repository": {
6
6
  "url": "https://github.com/invopop/popui"
7
7
  },
@@ -106,6 +106,7 @@
106
106
  "svelte-intersection-observer-action": "^0.0.4",
107
107
  "svelte-portal": "^2.2.1",
108
108
  "svelte-sonner": "^1.0.5",
109
+ "svelte-toolbelt": "^0.10.6",
109
110
  "svelte-transition": "^0.0.17",
110
111
  "svelte-viewport-info": "^1.0.2",
111
112
  "tailwind-variants": "^1.0.0",