@invopop/popui 0.1.93 → 0.1.95

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.
@@ -1,22 +1,13 @@
1
1
  <script lang="ts">
2
2
  import MenuItem from './MenuItem.svelte'
3
- import { flip, shift, offset } from 'svelte-floating-ui/dom'
4
- import { createFloatingActions } from 'svelte-floating-ui'
5
3
  import clsx from 'clsx'
6
- import type { MenuItemProps, DrawerOption, AnyProp } from './types.ts'
4
+ import { cn } from './utils.js'
5
+ import type { MenuItemProps } from './types.ts'
7
6
  import { Icon, type IconSource } from '@steeze-ui/svelte-icon'
8
7
  import { ChevronDown, ChevronRight } from '@invopop/ui-icons'
9
- import { FolderL } from '@invopop/ui-icons'
10
8
  import { resolveIcon } from './helpers.js'
11
- import DrawerContext from './DrawerContext.svelte'
12
9
  import TagBeta from './TagBeta.svelte'
13
10
 
14
- const [floatingRef, floatingContent] = createFloatingActions({
15
- strategy: 'absolute',
16
- placement: 'bottom-start',
17
- middleware: [offset(-4), flip(), shift()]
18
- })
19
-
20
11
  let {
21
12
  label = '',
22
13
  url = '',
@@ -24,50 +15,31 @@
24
15
  collapsable = false,
25
16
  open = $bindable(false),
26
17
  active = false,
27
- collapsedSidebar = false,
28
18
  iconTheme = 'default',
29
19
  icon = undefined,
20
+ imageUrl = undefined,
30
21
  beta = false,
31
22
  children = undefined,
23
+ action,
24
+ ref = $bindable(null),
25
+ class: className,
32
26
  onclick
33
27
  }: MenuItemProps = $props()
34
28
 
35
29
  let resolvedIcon: IconSource | undefined = $state()
36
- let hovered = $state(false)
37
- let highlight = $state(false)
38
- let leaveHoverTimeout: ReturnType<typeof setTimeout> | null = null
39
30
  let itemStyles = $derived(
40
- clsx(
41
- { 'text-foreground-inverse font-medium': !isFolderItem },
42
- { 'text-foreground-inverse-secondary': isFolderItem && !active },
43
- {
44
- 'border border-transparent hover:bg-background-selected-inverse group p-[7px] size-8':
45
- collapsedSidebar
46
- },
47
- { 'w-full px-2 py-1.5 h-8': !collapsedSidebar },
48
- {
49
- 'bg-background-selected-inverse text-white': active
50
- },
51
- { 'hover:bg-background-selected-inverse': !active }
52
- )
53
- )
54
- let iconStyles = $derived(
55
- clsx({ 'group-hover:text-white text-icon-inverse-bold!': collapsedSidebar })
31
+ clsx('flex-1 min-w-0 px-2 py-1.5 h-8', {
32
+ 'text-foreground-inverse font-medium': !isFolderItem,
33
+ 'text-foreground-inverse-secondary': isFolderItem && !active,
34
+ 'bg-background-selected-inverse text-white': active,
35
+ 'hover:bg-background-selected-inverse': !active
36
+ })
56
37
  )
57
38
  let wrapperStyles = $derived(
58
39
  clsx({
59
- 'ml-4 border-l border-white-10 pl-2 pt-0.5 relative': isFolderItem
40
+ 'group/menu-item ml-4 border-l border-white-10 pl-2 pt-0.5 relative': isFolderItem
60
41
  })
61
42
  )
62
- let items = $derived([
63
- { label, value: url, selected: active, icon: resolvedIcon },
64
- ...(children || []).map((c) => ({
65
- label: c.label || '',
66
- value: c.url || '',
67
- selected: c.active,
68
- icon: FolderL
69
- }))
70
- ] as DrawerOption[])
71
43
 
72
44
  $effect(() => {
73
45
  resolveIcon(icon).then((res) => (resolvedIcon = res))
@@ -80,84 +52,71 @@
80
52
 
81
53
  onclick?.(url)
82
54
  }
83
-
84
- function handleClickChild(value: AnyProp) {
85
- hovered = false
86
- onclick?.(value as string)
87
- }
88
-
89
- function handleHover() {
90
- highlight = true
91
- if (leaveHoverTimeout) {
92
- clearTimeout(leaveHoverTimeout)
93
- }
94
- hovered = true
95
- }
96
-
97
- function handleBlur() {
98
- highlight = false
99
- leaveHoverTimeout = setTimeout(() => {
100
- hovered = false
101
- }, 200)
102
- }
103
55
  </script>
104
56
 
105
- <div class={wrapperStyles}>
106
- {#if isFolderItem && (highlight || active)}
107
- <div class="border-l border-white h-3 w-px absolute top-3.5 left-0 -m-px"></div>
57
+ <div bind:this={ref} class={cn(wrapperStyles, className)} data-menu-item-root>
58
+ {#if isFolderItem}
59
+ <div
60
+ class={clsx('border-l border-white h-3 w-px absolute top-3.5 left-0 -m-px', {
61
+ 'opacity-0 group-hover/menu-item:opacity-100': !active
62
+ })}
63
+ data-menu-item-tree-indicator
64
+ ></div>
108
65
  {/if}
109
- <button
110
- use:floatingRef
111
- onmouseenter={handleHover}
112
- onmouseleave={handleBlur}
113
- onclick={handleClick}
114
- title={label}
115
- class="cursor-pointer {itemStyles} text-base border border-transparent flex items-center justify-between hover:text-white focus:text-white rounded-lg"
116
- >
117
- <span class="flex items-center space-x-1.5">
118
- {#if resolvedIcon}
119
- <Icon src={resolvedIcon} theme={iconTheme} class="{iconStyles} h-4 w-4 text-icon-inverse" />
120
- {/if}
121
- {#if !collapsedSidebar}
122
- <span class="whitespace-nowrap tracking-normal">{label}</span>
66
+ <div class={clsx('flex items-center', { 'gap-1.5': action })} data-menu-item-row>
67
+ <button
68
+ onclick={handleClick}
69
+ title={label}
70
+ data-menu-item-button
71
+ class="cursor-pointer {itemStyles} text-base border border-transparent flex items-center justify-between hover:text-white focus:text-white rounded-lg"
72
+ >
73
+ <span class="flex items-center space-x-1.5 min-w-0 flex-1" data-menu-item-content>
74
+ {#if imageUrl}
75
+ <img
76
+ src={imageUrl}
77
+ alt={label}
78
+ class="size-4 shrink-0 rounded bg-white object-contain"
79
+ data-menu-item-image
80
+ />
81
+ {:else if resolvedIcon}
82
+ <Icon
83
+ src={resolvedIcon}
84
+ theme={iconTheme}
85
+ class="h-4 w-4 text-icon-inverse"
86
+ data-menu-item-icon
87
+ />
88
+ {/if}
89
+ <span class="truncate tracking-normal" data-menu-item-label>{label}</span>
123
90
  {#if beta}
124
91
  <TagBeta />
125
92
  {/if}
126
- {/if}
127
- </span>
128
- {#if collapsable && !collapsedSidebar}
129
- <button
130
- class="cursor-pointer"
131
- onclick={(e) => {
132
- e.stopPropagation()
133
- open = !open
134
- }}
135
- >
136
- <Icon src={open ? ChevronDown : ChevronRight} class="h-4 w-4 text-white-40" />
137
- </button>
138
- {/if}
139
- </button>
140
- {#if children?.length}
141
- {#if collapsedSidebar}
142
- {#if hovered}
143
- <div
144
- use:floatingContent
145
- role="contentinfo"
146
- onmouseenter={handleHover}
147
- onmouseleave={handleBlur}
148
- class="pt-4 z-30"
93
+ </span>
94
+ {#if collapsable}
95
+ <button
96
+ class="cursor-pointer"
97
+ data-menu-item-chevron
98
+ onclick={(e) => {
99
+ e.stopPropagation()
100
+ open = !open
101
+ }}
149
102
  >
150
- <DrawerContext autofocus onclick={handleClickChild} {items} />
151
- </div>
103
+ <Icon src={open ? ChevronDown : ChevronRight} class="h-4 w-4 text-white-40" />
104
+ </button>
152
105
  {/if}
153
- {:else if open || !collapsable}
154
- <ul>
155
- {#each children as child}
156
- <li>
157
- <MenuItem {...child} isFolderItem {onclick} />
158
- </li>
159
- {/each}
160
- </ul>
106
+ </button>
107
+ {#if action}
108
+ <span class="shrink-0" data-menu-item-action>
109
+ {@render action()}
110
+ </span>
161
111
  {/if}
112
+ </div>
113
+ {#if children?.length && (open || !collapsable)}
114
+ <ul data-menu-item-children>
115
+ {#each children as child}
116
+ <li>
117
+ <MenuItem {...child} isFolderItem {onclick} />
118
+ </li>
119
+ {/each}
120
+ </ul>
162
121
  {/if}
163
122
  </div>
@@ -1,5 +1,5 @@
1
1
  import MenuItem from './MenuItem.svelte';
2
2
  import type { MenuItemProps } from './types.ts';
3
- declare const MenuItem: import("svelte").Component<MenuItemProps, {}, "open">;
3
+ declare const MenuItem: import("svelte").Component<MenuItemProps, {}, "open" | "ref">;
4
4
  type MenuItem = ReturnType<typeof MenuItem>;
5
5
  export default MenuItem;
@@ -18,7 +18,7 @@
18
18
  bind:ref
19
19
  data-slot="alert-dialog-content"
20
20
  class={cn(
21
- 'bg-background fixed left-1/2 top-1/2 z-[1002] flex flex-col w-full max-w-lg -translate-x-1/2 -translate-y-1/2 rounded-xl overflow-clip',
21
+ 'bg-background border border-border fixed left-1/2 top-1/2 z-[1002] flex flex-col w-full max-w-lg -translate-x-1/2 -translate-y-1/2 rounded-xl overflow-clip',
22
22
  className
23
23
  )}
24
24
  >
package/dist/types.d.ts CHANGED
@@ -533,11 +533,14 @@ export interface MenuItemProps {
533
533
  collapsable?: boolean;
534
534
  open?: boolean;
535
535
  active?: boolean;
536
- collapsedSidebar?: boolean;
537
536
  iconTheme?: IconTheme;
538
537
  icon?: IconSource | string | undefined;
538
+ imageUrl?: string;
539
539
  beta?: boolean;
540
540
  children?: MenuItemProps[] | undefined;
541
+ action?: Snippet;
542
+ ref?: HTMLDivElement | null;
543
+ class?: string;
541
544
  onclick?: (url: string) => void;
542
545
  }
543
546
  export interface MenuItemCollapsibleProps {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@invopop/popui",
3
3
  "license": "MIT",
4
- "version": "0.1.93",
4
+ "version": "0.1.95",
5
5
  "repository": {
6
6
  "url": "https://github.com/invopop/popui"
7
7
  },