@invopop/popui 0.1.94 → 0.1.96

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,40 @@
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
- let itemStyles = $derived(
30
+ let rowStyles = $derived(
31
+ clsx('flex items-center rounded-lg border border-transparent p-1', {
32
+ 'gap-1.5': action || collapsable,
33
+ 'bg-background-selected-inverse': active,
34
+ 'hover:bg-background-selected-inverse': !active
35
+ })
36
+ )
37
+ let buttonStyles = $derived(
40
38
  clsx(
41
- { 'text-foreground-inverse font-medium': !isFolderItem },
42
- { 'text-foreground-inverse-secondary': isFolderItem && !active },
39
+ 'flex-1 min-w-0 h-6 cursor-pointer text-base flex items-center hover:text-white focus:text-white',
43
40
  {
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 }
41
+ 'text-foreground-inverse font-medium': !isFolderItem,
42
+ 'text-foreground-inverse-secondary': isFolderItem && !active,
43
+ 'text-white': active
44
+ }
52
45
  )
53
46
  )
54
- let iconStyles = $derived(
55
- clsx({ 'group-hover:text-white text-icon-inverse-bold!': collapsedSidebar })
56
- )
57
47
  let wrapperStyles = $derived(
58
48
  clsx({
59
- 'ml-4 border-l border-white-10 pl-2 pt-0.5 relative': isFolderItem
49
+ 'group/menu-item ml-4 border-l border-white-10 pl-2 pt-0.5 relative': isFolderItem
60
50
  })
61
51
  )
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
52
 
72
53
  $effect(() => {
73
54
  resolveIcon(icon).then((res) => (resolvedIcon = res))
@@ -80,84 +61,68 @@
80
61
 
81
62
  onclick?.(url)
82
63
  }
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
64
  </script>
104
65
 
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>
66
+ <div bind:this={ref} class={cn(wrapperStyles, className)} data-menu-item-root>
67
+ {#if isFolderItem}
68
+ <div
69
+ class={clsx('border-l border-white h-3 w-px absolute top-3.5 left-0 -m-px', {
70
+ 'opacity-0 group-hover/menu-item:opacity-100': !active
71
+ })}
72
+ data-menu-item-tree-indicator
73
+ ></div>
108
74
  {/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>
75
+ <div class={rowStyles} data-menu-item-row>
76
+ <button
77
+ onclick={handleClick}
78
+ title={label}
79
+ data-menu-item-button
80
+ class={buttonStyles}
81
+ >
82
+ <span class="flex items-center space-x-1.5 min-w-0 flex-1" data-menu-item-content>
83
+ {#if imageUrl}
84
+ <img
85
+ src={imageUrl}
86
+ alt={label}
87
+ class="size-4 shrink-0 rounded bg-white object-contain"
88
+ data-menu-item-image
89
+ />
90
+ {:else if resolvedIcon}
91
+ <Icon
92
+ src={resolvedIcon}
93
+ theme={iconTheme}
94
+ class="h-4 w-4 text-icon-inverse"
95
+ data-menu-item-icon
96
+ />
97
+ {/if}
98
+ <span class="truncate tracking-normal" data-menu-item-label>{label}</span>
123
99
  {#if beta}
124
100
  <TagBeta />
125
101
  {/if}
126
- {/if}
127
- </span>
128
- {#if collapsable && !collapsedSidebar}
102
+ </span>
103
+ </button>
104
+ {#if action}
105
+ <span class="shrink-0" data-menu-item-action>
106
+ {@render action()}
107
+ </span>
108
+ {/if}
109
+ {#if collapsable}
129
110
  <button
130
- class="cursor-pointer"
131
- onclick={(e) => {
132
- e.stopPropagation()
133
- open = !open
134
- }}
111
+ class="shrink-0 cursor-pointer"
112
+ data-menu-item-chevron
113
+ onclick={() => (open = !open)}
135
114
  >
136
115
  <Icon src={open ? ChevronDown : ChevronRight} class="h-4 w-4 text-white-40" />
137
116
  </button>
138
117
  {/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"
149
- >
150
- <DrawerContext autofocus onclick={handleClickChild} {items} />
151
- </div>
152
- {/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>
161
- {/if}
118
+ </div>
119
+ {#if children?.length && (open || !collapsable)}
120
+ <ul data-menu-item-children>
121
+ {#each children as child}
122
+ <li>
123
+ <MenuItem {...child} isFolderItem {onclick} />
124
+ </li>
125
+ {/each}
126
+ </ul>
162
127
  {/if}
163
128
  </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;
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.94",
4
+ "version": "0.1.96",
5
5
  "repository": {
6
6
  "url": "https://github.com/invopop/popui"
7
7
  },