@invopop/popui 0.1.96 → 0.1.97
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/dist/CounterWidget.svelte +48 -30
- package/dist/MenuItem.svelte +106 -29
- package/dist/ProgressBarCircle.svelte +9 -6
- package/dist/types.d.ts +3 -1
- package/package.json +1 -1
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { Icon } from '@steeze-ui/svelte-icon'
|
|
4
4
|
import { Warning, Failure } from '@invopop/ui-icons'
|
|
5
5
|
import ProgressBar from './ProgressBar.svelte'
|
|
6
|
+
import ProgressBarCircle from './ProgressBarCircle.svelte'
|
|
6
7
|
|
|
7
8
|
let {
|
|
8
9
|
label,
|
|
@@ -10,43 +11,60 @@
|
|
|
10
11
|
total,
|
|
11
12
|
resetDate = '',
|
|
12
13
|
icon,
|
|
13
|
-
allowOverage = true
|
|
14
|
+
allowOverage = true,
|
|
15
|
+
collapsed = false
|
|
14
16
|
}: CounterWidgetProps = $props()
|
|
15
17
|
|
|
16
|
-
let
|
|
18
|
+
let progress = $derived(total > 0 ? current / total : 0)
|
|
19
|
+
let percentage = $derived(progress * 100)
|
|
17
20
|
let isOverage = $derived(current > total && allowOverage)
|
|
18
21
|
let isCritical = $derived(percentage >= 100 || (current > total && !allowOverage))
|
|
19
22
|
let isWarning = $derived(percentage >= 80 && percentage < 100)
|
|
23
|
+
let circleVariant = $derived.by<'dark' | 'warning' | 'critical'>(() => {
|
|
24
|
+
if (isCritical) return 'critical'
|
|
25
|
+
if (isWarning) return 'warning'
|
|
26
|
+
return 'dark'
|
|
27
|
+
})
|
|
20
28
|
</script>
|
|
21
29
|
|
|
22
|
-
|
|
23
|
-
<div
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
30
|
+
{#if collapsed}
|
|
31
|
+
<div
|
|
32
|
+
class="size-8 p-1.75 flex items-center justify-center rounded-lg border border-transparent hover:bg-background-selected-inverse"
|
|
33
|
+
title="{label}: {current}/{total}"
|
|
34
|
+
data-counter-widget-collapsed
|
|
35
|
+
>
|
|
36
|
+
<ProgressBarCircle {progress} size={16} variant={circleVariant} />
|
|
37
|
+
</div>
|
|
38
|
+
{:else}
|
|
39
|
+
<div class="border border-border-inverse rounded-xl w-full py-[5px] pl-2 pr-1.5 flex flex-col">
|
|
40
|
+
<div class="flex items-center justify-between text-base font-medium">
|
|
41
|
+
<div class="flex items-center gap-1.5">
|
|
42
|
+
{#if icon}
|
|
43
|
+
<Icon src={icon} class="size-3 text-icon-inverse rounded-xs" />
|
|
44
|
+
{/if}
|
|
45
|
+
<span class="font-medium text-foreground-inverse">
|
|
46
|
+
{label}
|
|
47
|
+
</span>
|
|
48
|
+
</div>
|
|
49
|
+
<div class="flex items-center gap-1.5">
|
|
50
|
+
{#if isCritical && !isOverage}
|
|
51
|
+
<Icon src={Failure} class="size-4 text-icon-critical-inverse" />
|
|
52
|
+
{:else if isWarning && !isOverage}
|
|
53
|
+
<Icon src={Warning} class="size-4 text-icon-warning-inverse" />
|
|
54
|
+
{/if}
|
|
55
|
+
<span class="font-mono text-foreground-inverse">
|
|
56
|
+
{current}<span class="text-white/70">/{total}</span>
|
|
57
|
+
</span>
|
|
58
|
+
</div>
|
|
31
59
|
</div>
|
|
32
|
-
<div class="
|
|
33
|
-
{
|
|
34
|
-
<Icon src={Failure} class="size-4 text-icon-critical-inverse" />
|
|
35
|
-
{:else if isWarning && !isOverage}
|
|
36
|
-
<Icon src={Warning} class="size-4 text-icon-warning-inverse" />
|
|
37
|
-
{/if}
|
|
38
|
-
<span class="font-mono text-foreground-inverse">
|
|
39
|
-
{current}<span class="text-white/70">/{total}</span>
|
|
40
|
-
</span>
|
|
60
|
+
<div class="py-2">
|
|
61
|
+
<ProgressBar {percentage} {current} {total} {allowOverage} />
|
|
41
62
|
</div>
|
|
42
|
-
</div>
|
|
43
|
-
<div class="py-2">
|
|
44
|
-
<ProgressBar {percentage} {current} {total} {allowOverage} />
|
|
45
|
-
</div>
|
|
46
63
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
</div>
|
|
64
|
+
{#if resetDate}
|
|
65
|
+
<span class="text-sm font-normal text-foreground-inverse-secondary">
|
|
66
|
+
{label} reset {resetDate}
|
|
67
|
+
</span>
|
|
68
|
+
{/if}
|
|
69
|
+
</div>
|
|
70
|
+
{/if}
|
package/dist/MenuItem.svelte
CHANGED
|
@@ -1,13 +1,22 @@
|
|
|
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'
|
|
3
5
|
import clsx from 'clsx'
|
|
4
6
|
import { cn } from './utils.js'
|
|
5
|
-
import type { MenuItemProps } from './types.ts'
|
|
7
|
+
import type { MenuItemProps, DrawerOption, AnyProp } from './types.ts'
|
|
6
8
|
import { Icon, type IconSource } from '@steeze-ui/svelte-icon'
|
|
7
|
-
import { ChevronDown, ChevronRight } from '@invopop/ui-icons'
|
|
9
|
+
import { ChevronDown, ChevronRight, FolderL } from '@invopop/ui-icons'
|
|
8
10
|
import { resolveIcon } from './helpers.js'
|
|
11
|
+
import DrawerContext from './DrawerContext.svelte'
|
|
9
12
|
import TagBeta from './TagBeta.svelte'
|
|
10
13
|
|
|
14
|
+
const [floatingRef, floatingContent] = createFloatingActions({
|
|
15
|
+
strategy: 'absolute',
|
|
16
|
+
placement: 'bottom-start',
|
|
17
|
+
middleware: [offset(-4), flip(), shift()]
|
|
18
|
+
})
|
|
19
|
+
|
|
11
20
|
let {
|
|
12
21
|
label = '',
|
|
13
22
|
url = '',
|
|
@@ -15,6 +24,7 @@
|
|
|
15
24
|
collapsable = false,
|
|
16
25
|
open = $bindable(false),
|
|
17
26
|
active = false,
|
|
27
|
+
collapsedSidebar = false,
|
|
18
28
|
iconTheme = 'default',
|
|
19
29
|
icon = undefined,
|
|
20
30
|
imageUrl = undefined,
|
|
@@ -27,28 +37,46 @@
|
|
|
27
37
|
}: MenuItemProps = $props()
|
|
28
38
|
|
|
29
39
|
let resolvedIcon: IconSource | undefined = $state()
|
|
40
|
+
let hovered = $state(false)
|
|
41
|
+
let highlight = $state(false)
|
|
42
|
+
let leaveHoverTimeout: ReturnType<typeof setTimeout> | null = null
|
|
30
43
|
let rowStyles = $derived(
|
|
31
|
-
clsx('flex items-center rounded-lg border border-transparent
|
|
32
|
-
'
|
|
44
|
+
clsx('flex items-center rounded-lg border border-transparent', {
|
|
45
|
+
'py-1 pr-1': !collapsedSidebar,
|
|
46
|
+
'pl-2': !collapsedSidebar && !imageUrl,
|
|
47
|
+
'pl-[10px]': !collapsedSidebar && imageUrl,
|
|
48
|
+
'gap-1.5': !collapsedSidebar && (action || collapsable),
|
|
49
|
+
'size-8 p-[7px] justify-center': collapsedSidebar,
|
|
33
50
|
'bg-background-selected-inverse': active,
|
|
34
51
|
'hover:bg-background-selected-inverse': !active
|
|
35
52
|
})
|
|
36
53
|
)
|
|
37
54
|
let buttonStyles = $derived(
|
|
38
|
-
clsx(
|
|
39
|
-
'flex-1 min-w-0 h-6
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
55
|
+
clsx('cursor-pointer text-base flex items-center hover:text-white focus:text-white', {
|
|
56
|
+
'flex-1 min-w-0 h-6': !collapsedSidebar,
|
|
57
|
+
'group': collapsedSidebar,
|
|
58
|
+
'text-foreground-inverse font-medium': !isFolderItem,
|
|
59
|
+
'text-foreground-inverse-secondary': isFolderItem && !active,
|
|
60
|
+
'text-white': active
|
|
61
|
+
})
|
|
62
|
+
)
|
|
63
|
+
let iconStyles = $derived(
|
|
64
|
+
clsx({ 'group-hover:text-white text-icon-inverse-bold!': collapsedSidebar })
|
|
46
65
|
)
|
|
47
66
|
let wrapperStyles = $derived(
|
|
48
67
|
clsx({
|
|
49
68
|
'group/menu-item ml-4 border-l border-white-10 pl-2 pt-0.5 relative': isFolderItem
|
|
50
69
|
})
|
|
51
70
|
)
|
|
71
|
+
let items = $derived([
|
|
72
|
+
{ label, value: url, selected: active, icon: resolvedIcon },
|
|
73
|
+
...(children || []).map((c) => ({
|
|
74
|
+
label: c.label || '',
|
|
75
|
+
value: c.url || '',
|
|
76
|
+
selected: c.active,
|
|
77
|
+
icon: FolderL
|
|
78
|
+
}))
|
|
79
|
+
] as DrawerOption[])
|
|
52
80
|
|
|
53
81
|
$effect(() => {
|
|
54
82
|
resolveIcon(icon).then((res) => (resolvedIcon = res))
|
|
@@ -61,25 +89,58 @@
|
|
|
61
89
|
|
|
62
90
|
onclick?.(url)
|
|
63
91
|
}
|
|
92
|
+
|
|
93
|
+
function handleClickChild(value: AnyProp) {
|
|
94
|
+
hovered = false
|
|
95
|
+
onclick?.(value as string)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function handleHover() {
|
|
99
|
+
highlight = true
|
|
100
|
+
if (leaveHoverTimeout) {
|
|
101
|
+
clearTimeout(leaveHoverTimeout)
|
|
102
|
+
}
|
|
103
|
+
hovered = true
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function handleBlur() {
|
|
107
|
+
highlight = false
|
|
108
|
+
leaveHoverTimeout = setTimeout(() => {
|
|
109
|
+
hovered = false
|
|
110
|
+
}, 200)
|
|
111
|
+
}
|
|
64
112
|
</script>
|
|
65
113
|
|
|
66
114
|
<div bind:this={ref} class={cn(wrapperStyles, className)} data-menu-item-root>
|
|
67
115
|
{#if isFolderItem}
|
|
68
116
|
<div
|
|
69
117
|
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
|
|
118
|
+
'opacity-0 group-hover/menu-item:opacity-100': !active && !collapsedSidebar,
|
|
119
|
+
'opacity-0': collapsedSidebar && !highlight && !active
|
|
71
120
|
})}
|
|
72
121
|
data-menu-item-tree-indicator
|
|
73
122
|
></div>
|
|
74
123
|
{/if}
|
|
75
|
-
<div
|
|
124
|
+
<div
|
|
125
|
+
class={rowStyles}
|
|
126
|
+
data-menu-item-row
|
|
127
|
+
use:floatingRef
|
|
128
|
+
onmouseenter={collapsedSidebar ? handleHover : undefined}
|
|
129
|
+
onmouseleave={collapsedSidebar ? handleBlur : undefined}
|
|
130
|
+
role={collapsedSidebar ? 'presentation' : undefined}
|
|
131
|
+
>
|
|
76
132
|
<button
|
|
77
133
|
onclick={handleClick}
|
|
78
134
|
title={label}
|
|
79
135
|
data-menu-item-button
|
|
80
136
|
class={buttonStyles}
|
|
81
137
|
>
|
|
82
|
-
<span
|
|
138
|
+
<span
|
|
139
|
+
class={clsx('flex items-center', {
|
|
140
|
+
'space-x-1.5 min-w-0 flex-1': !collapsedSidebar
|
|
141
|
+
})}
|
|
142
|
+
data-menu-item-content
|
|
143
|
+
>
|
|
83
144
|
{#if imageUrl}
|
|
84
145
|
<img
|
|
85
146
|
src={imageUrl}
|
|
@@ -91,22 +152,24 @@
|
|
|
91
152
|
<Icon
|
|
92
153
|
src={resolvedIcon}
|
|
93
154
|
theme={iconTheme}
|
|
94
|
-
class="h-4 w-4 text-icon-inverse"
|
|
155
|
+
class="{iconStyles} h-4 w-4 text-icon-inverse"
|
|
95
156
|
data-menu-item-icon
|
|
96
157
|
/>
|
|
97
158
|
{/if}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
159
|
+
{#if !collapsedSidebar}
|
|
160
|
+
<span class="truncate tracking-normal" data-menu-item-label>{label}</span>
|
|
161
|
+
{#if beta}
|
|
162
|
+
<TagBeta />
|
|
163
|
+
{/if}
|
|
101
164
|
{/if}
|
|
102
165
|
</span>
|
|
103
166
|
</button>
|
|
104
|
-
{#if action}
|
|
167
|
+
{#if !collapsedSidebar && action}
|
|
105
168
|
<span class="shrink-0" data-menu-item-action>
|
|
106
169
|
{@render action()}
|
|
107
170
|
</span>
|
|
108
171
|
{/if}
|
|
109
|
-
{#if collapsable}
|
|
172
|
+
{#if !collapsedSidebar && collapsable}
|
|
110
173
|
<button
|
|
111
174
|
class="shrink-0 cursor-pointer"
|
|
112
175
|
data-menu-item-chevron
|
|
@@ -116,13 +179,27 @@
|
|
|
116
179
|
</button>
|
|
117
180
|
{/if}
|
|
118
181
|
</div>
|
|
119
|
-
{#if children?.length
|
|
120
|
-
|
|
121
|
-
{#
|
|
122
|
-
<
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
182
|
+
{#if children?.length}
|
|
183
|
+
{#if collapsedSidebar}
|
|
184
|
+
{#if hovered}
|
|
185
|
+
<div
|
|
186
|
+
use:floatingContent
|
|
187
|
+
role="contentinfo"
|
|
188
|
+
onmouseenter={handleHover}
|
|
189
|
+
onmouseleave={handleBlur}
|
|
190
|
+
class="pt-4 z-30"
|
|
191
|
+
>
|
|
192
|
+
<DrawerContext autofocus onclick={handleClickChild} {items} />
|
|
193
|
+
</div>
|
|
194
|
+
{/if}
|
|
195
|
+
{:else if open || !collapsable}
|
|
196
|
+
<ul data-menu-item-children>
|
|
197
|
+
{#each children as child}
|
|
198
|
+
<li>
|
|
199
|
+
<MenuItem {...child} isFolderItem {onclick} />
|
|
200
|
+
</li>
|
|
201
|
+
{/each}
|
|
202
|
+
</ul>
|
|
203
|
+
{/if}
|
|
127
204
|
{/if}
|
|
128
205
|
</div>
|
|
@@ -4,13 +4,16 @@
|
|
|
4
4
|
let { progress, size, variant = 'default' }: ProgressBarCircleProps = $props()
|
|
5
5
|
|
|
6
6
|
let angle = $derived(360 * progress)
|
|
7
|
-
let filledColor = $derived(
|
|
8
|
-
variant === '
|
|
9
|
-
|
|
7
|
+
let filledColor = $derived.by(() => {
|
|
8
|
+
if (variant === 'critical') return 'var(--color-background-critical-inverse)'
|
|
9
|
+
if (variant === 'warning') return 'var(--color-background-warning-inverse)'
|
|
10
|
+
if (variant === 'dark') return 'var(--color-icon-inverse-bold)'
|
|
11
|
+
return 'var(--color-icon-selected-default)'
|
|
12
|
+
})
|
|
10
13
|
let restColor = $derived(
|
|
11
|
-
variant === '
|
|
12
|
-
? 'var(--color-background-
|
|
13
|
-
: 'var(--color-background-
|
|
14
|
+
variant === 'default'
|
|
15
|
+
? 'var(--color-background-default-tertiary)'
|
|
16
|
+
: 'var(--color-background-selected-inverse-hover)'
|
|
14
17
|
)
|
|
15
18
|
let background = $derived(
|
|
16
19
|
`conic-gradient(${filledColor} 0deg ${angle}deg, ${restColor} ${angle}deg 360deg)`
|
package/dist/types.d.ts
CHANGED
|
@@ -200,6 +200,7 @@ export interface CounterWidgetProps {
|
|
|
200
200
|
resetDate?: string;
|
|
201
201
|
icon?: IconSource;
|
|
202
202
|
allowOverage?: boolean;
|
|
203
|
+
collapsed?: boolean;
|
|
203
204
|
}
|
|
204
205
|
export interface ProgressBarProps {
|
|
205
206
|
percentage?: number;
|
|
@@ -211,7 +212,7 @@ export interface ProgressBarProps {
|
|
|
211
212
|
export interface ProgressBarCircleProps {
|
|
212
213
|
progress: number;
|
|
213
214
|
size: number;
|
|
214
|
-
variant?: 'default' | 'dark';
|
|
215
|
+
variant?: 'default' | 'dark' | 'warning' | 'critical';
|
|
215
216
|
}
|
|
216
217
|
export interface TagProgressProps {
|
|
217
218
|
progress: number;
|
|
@@ -533,6 +534,7 @@ export interface MenuItemProps {
|
|
|
533
534
|
collapsable?: boolean;
|
|
534
535
|
open?: boolean;
|
|
535
536
|
active?: boolean;
|
|
537
|
+
collapsedSidebar?: boolean;
|
|
536
538
|
iconTheme?: IconTheme;
|
|
537
539
|
icon?: IconSource | string | undefined;
|
|
538
540
|
imageUrl?: string;
|