lutra 0.0.18 → 0.0.20
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/data/Stat.svelte.d.ts +12 -6
- package/dist/display/Avatar.svelte.d.ts +5 -0
- package/dist/display/Badge.svelte.d.ts +10 -6
- package/dist/display/Callout.svelte +8 -4
- package/dist/display/Callout.svelte.d.ts +11 -9
- package/dist/display/Close.svelte +58 -0
- package/dist/display/Close.svelte.d.ts +21 -0
- package/dist/display/Code.svelte.d.ts +10 -8
- package/dist/display/ContextTip.svelte +1 -2
- package/dist/display/ContextTip.svelte.d.ts +5 -0
- package/dist/display/Details.svelte.d.ts +11 -5
- package/dist/display/Hero.svelte.d.ts +9 -4
- package/dist/display/Icon.svelte +4 -6
- package/dist/display/Icon.svelte.d.ts +6 -0
- package/dist/display/IconButton.svelte +2 -3
- package/dist/display/IconButton.svelte.d.ts +8 -4
- package/dist/display/Image.svelte +63 -13
- package/dist/display/Image.svelte.d.ts +15 -2
- package/dist/display/Indicator.svelte.d.ts +5 -3
- package/dist/display/Inset.svelte.d.ts +5 -0
- package/dist/display/Notification.svelte +104 -0
- package/dist/display/Notification.svelte.d.ts +42 -0
- package/dist/display/Popup.svelte.d.ts +10 -4
- package/dist/display/Table.svelte +3 -0
- package/dist/display/Table.svelte.d.ts +11 -0
- package/dist/display/Tag.svelte.d.ts +11 -7
- package/dist/display/Tooltip.svelte +1 -1
- package/dist/display/Tooltip.svelte.d.ts +9 -3
- package/dist/display/notifications.svelte.d.ts +21 -0
- package/dist/display/notifications.svelte.js +31 -0
- package/dist/form/Button.svelte.d.ts +12 -0
- package/dist/form/FieldActions.svelte +1 -1
- package/dist/form/FieldActions.svelte.d.ts +6 -0
- package/dist/form/FieldContainer.svelte +5 -0
- package/dist/form/FieldContainer.svelte.d.ts +6 -0
- package/dist/form/FieldContent.svelte +3 -0
- package/dist/form/FieldContent.svelte.d.ts +23 -0
- package/dist/form/FieldError.svelte.d.ts +4 -0
- package/dist/form/FieldSection.svelte +13 -1
- package/dist/form/FieldSection.svelte.d.ts +11 -0
- package/dist/form/Fieldset.svelte.d.ts +15 -7
- package/dist/form/Form.svelte +15 -12
- package/dist/form/Form.svelte.d.ts +10 -0
- package/dist/form/Input.svelte +4 -2
- package/dist/form/Input.svelte.d.ts +66 -55
- package/dist/form/InputLength.svelte.d.ts +4 -2
- package/dist/form/Label.svelte.d.ts +9 -3
- package/dist/form/Select.svelte +0 -1
- package/dist/form/Select.svelte.d.ts +44 -27
- package/dist/form/form.js +1 -1
- package/dist/grid/Column.svelte.d.ts +5 -0
- package/dist/grid/Grid.svelte.d.ts +6 -0
- package/dist/grid/Row.svelte.d.ts +6 -1
- package/dist/layout/Layout.svelte +2 -0
- package/dist/layout/Layout.svelte.d.ts +6 -2
- package/dist/layout/LayoutFooter.svelte.d.ts +3 -1
- package/dist/layout/LayoutGrid.svelte.d.ts +13 -5
- package/dist/layout/LayoutHeader.svelte.d.ts +13 -12
- package/dist/layout/LayoutSideMenu.svelte +54 -0
- package/dist/layout/LayoutSideMenu.svelte.d.ts +25 -0
- package/dist/layout/Overlay.svelte +20 -0
- package/dist/layout/Overlay.svelte.d.ts +35 -0
- package/dist/layout/OverlayContainer.svelte +28 -0
- package/dist/layout/OverlayContainer.svelte.d.ts +16 -0
- package/dist/layout/OverlayLayer.svelte +145 -0
- package/dist/layout/OverlayLayer.svelte.d.ts +22 -0
- package/dist/layout/PageContent.svelte +9 -1
- package/dist/layout/PageContent.svelte.d.ts +8 -3
- package/dist/layout/Theme.svelte +8 -0
- package/dist/layout/Theme.svelte.d.ts +6 -0
- package/dist/layout/UIContent.svelte.d.ts +5 -0
- package/dist/layout/overlays.svelte.d.ts +34 -0
- package/dist/layout/overlays.svelte.js +44 -0
- package/dist/nav/Breadcrumb.svelte.d.ts +9 -5
- package/dist/nav/Menu.svelte +43 -62
- package/dist/nav/Menu.svelte.d.ts +11 -4
- package/dist/nav/MenuItem.svelte +25 -7
- package/dist/nav/MenuItem.svelte.d.ts +9 -0
- package/dist/nav/NavMenu.svelte.d.ts +3 -1
- package/dist/nav/TabbedContent.svelte.d.ts +5 -3
- package/dist/nav/Tabs.svelte.d.ts +6 -4
- package/dist/style.css +74 -36
- package/dist/typo/Clamp.svelte.d.ts +8 -3
- package/dist/typo/H.svelte.d.ts +10 -5
- package/dist/typo/H1.svelte.d.ts +9 -4
- package/dist/typo/H2.svelte.d.ts +9 -4
- package/dist/typo/H3.svelte.d.ts +9 -4
- package/dist/typo/H4.svelte.d.ts +9 -4
- package/dist/typo/H5.svelte.d.ts +9 -4
- package/dist/typo/H6.svelte.d.ts +9 -4
- package/dist/typo/P.svelte.d.ts +9 -4
- package/dist/utils/StringOrComponentOrSnippet.svelte +3 -2
- package/dist/utils/StringOrComponentOrSnippet.svelte.d.ts +7 -0
- package/dist/utils/attr.d.ts +2 -2
- package/dist/utils/attr.js +2 -2
- package/dist/utils/dom.d.ts +15 -0
- package/dist/utils/dom.js +74 -0
- package/dist/utils/keyboard.svelte.d.ts +19 -0
- package/dist/utils/keyboard.svelte.js +22 -3
- package/dist/utils/transitions.d.ts +1 -0
- package/dist/utils/transitions.js +9 -2
- package/package.json +11 -9
@@ -0,0 +1,20 @@
|
|
1
|
+
<script lang="ts">import { onDestroy, onMount } from "svelte";
|
2
|
+
import { addOverlay, removeOverlay } from "./overlays.svelte.js";
|
3
|
+
import { createId } from "../utils/id.js";
|
4
|
+
let {
|
5
|
+
id = createId(),
|
6
|
+
children,
|
7
|
+
position = "center",
|
8
|
+
anchor,
|
9
|
+
layer,
|
10
|
+
transition,
|
11
|
+
z
|
12
|
+
} = $props();
|
13
|
+
onMount(() => {
|
14
|
+
addOverlay({ id, z, layer, content: children, transition, position, anchor });
|
15
|
+
});
|
16
|
+
onDestroy(() => {
|
17
|
+
removeOverlay(id);
|
18
|
+
});
|
19
|
+
</script>
|
20
|
+
|
@@ -0,0 +1,35 @@
|
|
1
|
+
import { SvelteComponent } from "svelte";
|
2
|
+
import { type Snippet } from "svelte";
|
3
|
+
import { type OverlayPosition, type TransitionOpts } from "./overlays.svelte.js";
|
4
|
+
declare const __propDef: {
|
5
|
+
props: {
|
6
|
+
id?: string | undefined;
|
7
|
+
children: Snippet;
|
8
|
+
anchor?: HTMLElement | undefined;
|
9
|
+
layer?: string | undefined;
|
10
|
+
position?: OverlayPosition | undefined;
|
11
|
+
transition?: TransitionOpts | undefined;
|
12
|
+
z?: number | undefined;
|
13
|
+
};
|
14
|
+
events: {
|
15
|
+
[evt: string]: CustomEvent<any>;
|
16
|
+
};
|
17
|
+
slots: {};
|
18
|
+
};
|
19
|
+
export type OverlayProps = typeof __propDef.props;
|
20
|
+
export type OverlayEvents = typeof __propDef.events;
|
21
|
+
export type OverlaySlots = typeof __propDef.slots;
|
22
|
+
export default class Overlay extends SvelteComponent<OverlayProps, OverlayEvents, OverlaySlots> {
|
23
|
+
constructor(options?: import("svelte").ComponentConstructorOptions<{
|
24
|
+
id?: string | undefined;
|
25
|
+
children: (this: void) => typeof import("svelte").SnippetReturn & {
|
26
|
+
_: "functions passed to {@render ...} tags must use the `Snippet` type imported from \"svelte\"";
|
27
|
+
};
|
28
|
+
anchor?: HTMLElement | undefined;
|
29
|
+
layer?: string | undefined;
|
30
|
+
position?: OverlayPosition | undefined;
|
31
|
+
transition?: TransitionOpts | undefined;
|
32
|
+
z?: number | undefined;
|
33
|
+
}>);
|
34
|
+
}
|
35
|
+
export {};
|
@@ -0,0 +1,28 @@
|
|
1
|
+
<script lang="ts">import { overlays } from "./overlays.svelte.js";
|
2
|
+
import OverlayLayer from "./OverlayLayer.svelte";
|
3
|
+
import { slidefade } from "../utils/transitions.js";
|
4
|
+
</script>
|
5
|
+
|
6
|
+
<div class="Overlay">
|
7
|
+
<OverlayLayer position="top left" items={overlays.topLeft} />
|
8
|
+
<OverlayLayer position="top right" items={overlays.topRight} />
|
9
|
+
<OverlayLayer position="top center" items={overlays.topCenter} />
|
10
|
+
<OverlayLayer position="bottom left" items={overlays.bottomLeft} />
|
11
|
+
<OverlayLayer position="bottom right" items={overlays.bottomRight} />
|
12
|
+
<OverlayLayer position="bottom center" items={overlays.bottomCenter} />
|
13
|
+
<OverlayLayer position="center" items={overlays.center} />
|
14
|
+
<OverlayLayer position="anchor" items={overlays.anchor} />
|
15
|
+
</div>
|
16
|
+
|
17
|
+
<style>
|
18
|
+
.Overlay {
|
19
|
+
display: block;
|
20
|
+
position: fixed;
|
21
|
+
top: 0;
|
22
|
+
left: 0;
|
23
|
+
right: 0;
|
24
|
+
bottom: 0;
|
25
|
+
z-index: 1000;
|
26
|
+
pointer-events: none;
|
27
|
+
}
|
28
|
+
</style>
|
@@ -0,0 +1,16 @@
|
|
1
|
+
import { SvelteComponent } from "svelte";
|
2
|
+
declare const __propDef: {
|
3
|
+
props: {
|
4
|
+
[x: string]: never;
|
5
|
+
};
|
6
|
+
events: {
|
7
|
+
[evt: string]: CustomEvent<any>;
|
8
|
+
};
|
9
|
+
slots: {};
|
10
|
+
};
|
11
|
+
export type OverlayContainerProps = typeof __propDef.props;
|
12
|
+
export type OverlayContainerEvents = typeof __propDef.events;
|
13
|
+
export type OverlayContainerSlots = typeof __propDef.slots;
|
14
|
+
export default class OverlayContainer extends SvelteComponent<OverlayContainerProps, OverlayContainerEvents, OverlayContainerSlots> {
|
15
|
+
}
|
16
|
+
export {};
|
@@ -0,0 +1,145 @@
|
|
1
|
+
<script lang="ts">import {} from "./overlays.svelte.js";
|
2
|
+
import { slidefade } from "../utils/transitions.js";
|
3
|
+
import { BROWSER } from "esm-env";
|
4
|
+
import { isComponent, isSnippet } from "../utils/isSnippet.js";
|
5
|
+
import { untrack } from "svelte";
|
6
|
+
let {
|
7
|
+
position,
|
8
|
+
items
|
9
|
+
} = $props();
|
10
|
+
const fudge = 8;
|
11
|
+
let contentEls = $state({});
|
12
|
+
let scrollX = $state(BROWSER ? window.scrollX : 0);
|
13
|
+
let scrollY = $state(BROWSER ? window.scrollY : 0);
|
14
|
+
let innerWidth = $state(BROWSER ? window.innerWidth : 0);
|
15
|
+
let innerHeight = $state(BROWSER ? window.innerHeight : 0);
|
16
|
+
let positions = $derived.by(() => {
|
17
|
+
innerWidth;
|
18
|
+
innerHeight;
|
19
|
+
scrollX;
|
20
|
+
scrollY;
|
21
|
+
contentEls;
|
22
|
+
return items.map((item) => {
|
23
|
+
const contentEl = contentEls[item.id];
|
24
|
+
if (!item.anchor || !contentEl)
|
25
|
+
return { left: 0, top: 0, index: 1 };
|
26
|
+
const triggerPos = item.anchor.getBoundingClientRect();
|
27
|
+
const height = contentEl.clientHeight;
|
28
|
+
const width = contentEl.clientWidth;
|
29
|
+
const isOffBottom = triggerPos.bottom + height > window.innerHeight - fudge;
|
30
|
+
const isOffRight = triggerPos.left + width > window.innerWidth - fudge;
|
31
|
+
let left = triggerPos.left;
|
32
|
+
let top = triggerPos.top + triggerPos.height + fudge;
|
33
|
+
if (isOffRight)
|
34
|
+
left = left - width + triggerPos.width;
|
35
|
+
if (isOffBottom)
|
36
|
+
top = top - height - triggerPos.height - fudge * 2;
|
37
|
+
return {
|
38
|
+
left,
|
39
|
+
top,
|
40
|
+
index: 1
|
41
|
+
};
|
42
|
+
});
|
43
|
+
});
|
44
|
+
let originCache = $state({});
|
45
|
+
let origins = $derived.by(() => {
|
46
|
+
return items.map((item, index) => {
|
47
|
+
if (!item.anchor || item.position !== "anchor") {
|
48
|
+
return item.position || "center";
|
49
|
+
}
|
50
|
+
const triggerPos = item.anchor.getBoundingClientRect();
|
51
|
+
const contentEl = document.getElementById(item.id);
|
52
|
+
const contentRect = contentEl?.getBoundingClientRect();
|
53
|
+
const height = contentRect.height;
|
54
|
+
const width = contentRect.width;
|
55
|
+
const isOffBottom = triggerPos.bottom + height > innerHeight - fudge;
|
56
|
+
const isOffRight = triggerPos.left + width > innerWidth - fudge;
|
57
|
+
let text = "top left";
|
58
|
+
if (isOffRight)
|
59
|
+
text = text.replace("left", "right");
|
60
|
+
if (isOffBottom)
|
61
|
+
text = text.replace("top", "bottom");
|
62
|
+
return text;
|
63
|
+
});
|
64
|
+
});
|
65
|
+
$effect(() => {
|
66
|
+
origins;
|
67
|
+
origins.forEach((origin, index) => {
|
68
|
+
originCache[untrack(() => items[index].id)] = origin;
|
69
|
+
});
|
70
|
+
});
|
71
|
+
function introstart(item) {
|
72
|
+
document.getElementById(item.id).style.pointerEvents = "none";
|
73
|
+
}
|
74
|
+
function introend(item) {
|
75
|
+
document.getElementById(item.id).style.pointerEvents = "auto";
|
76
|
+
}
|
77
|
+
</script>
|
78
|
+
|
79
|
+
<svelte:window bind:scrollX bind:scrollY bind:innerWidth bind:innerHeight />
|
80
|
+
|
81
|
+
<div class="Layer {position}">
|
82
|
+
{#each items as item, index (item.id)}
|
83
|
+
<div
|
84
|
+
id={item.id}
|
85
|
+
bind:this={contentEls[item.id]}
|
86
|
+
class="LayerItem"
|
87
|
+
class:anchor={item.anchor ? true : false}
|
88
|
+
onintrostart={() => introstart(item)}
|
89
|
+
onintroend={() => introend(item)}
|
90
|
+
transition:slidefade|global={{ duration: 150, origin: originCache[item.id] || origins[index], noMargin: !!!item.anchor }}
|
91
|
+
style="--index: {index}; --z: {item.z}; --left: {positions[index].left}px; --top: {positions[index].top}px;"
|
92
|
+
>
|
93
|
+
{#if isComponent(item.content)}
|
94
|
+
<svelte:component this={item.content} {...item.props} />
|
95
|
+
{:else if isSnippet(item.content)}
|
96
|
+
{@render item.content()}
|
97
|
+
{/if}
|
98
|
+
</div>
|
99
|
+
{/each}
|
100
|
+
</div>
|
101
|
+
|
102
|
+
<style>
|
103
|
+
.Layer {
|
104
|
+
pointer-events: auto;
|
105
|
+
position: absolute;
|
106
|
+
display: flex;
|
107
|
+
flex-direction: column-reverse;
|
108
|
+
gap: 0.75rem;
|
109
|
+
}
|
110
|
+
.Layer.center {
|
111
|
+
left: 50%;
|
112
|
+
right: auto;
|
113
|
+
transform: translateX(-50%);
|
114
|
+
}
|
115
|
+
.Layer.top {
|
116
|
+
top: calc(1rem + env(safe-area-inset-top));
|
117
|
+
bottom: unset;
|
118
|
+
}
|
119
|
+
.Layer.bottom {
|
120
|
+
top: unset;
|
121
|
+
bottom: calc(1rem + env(safe-area-inset-bottom));
|
122
|
+
}
|
123
|
+
.Layer.right {
|
124
|
+
left: unset;
|
125
|
+
right: calc(1rem + env(safe-area-inset-right));
|
126
|
+
}
|
127
|
+
.Layer.left {
|
128
|
+
left: calc(1rem + env(safe-area-inset-left));
|
129
|
+
right: unset;
|
130
|
+
}
|
131
|
+
.Layer.center:not(.top):not(.bottom):not(.anchor) {
|
132
|
+
top: 50%;
|
133
|
+
bottom: auto;
|
134
|
+
transform: translate(calc(-50% + env(safe-area-inset-left) + env(safe-area-inset-right)), calc(-50% + env(safe-area-inset-top) + env(safe-area-inset-bottom)));
|
135
|
+
}
|
136
|
+
.LayerItem {
|
137
|
+
position: relative;
|
138
|
+
z-index: calc(100 + var(--z, 1) - var(--index, 0));
|
139
|
+
}
|
140
|
+
.LayerItem.anchor {
|
141
|
+
position: absolute;
|
142
|
+
top: var(--top, 0);
|
143
|
+
left: var(--left, 0);
|
144
|
+
}
|
145
|
+
</style>
|
@@ -0,0 +1,22 @@
|
|
1
|
+
import { SvelteComponent } from "svelte";
|
2
|
+
import { type OverlayItem, type OverlayPosition } from "./overlays.svelte.js";
|
3
|
+
declare const __propDef: {
|
4
|
+
props: {
|
5
|
+
position?: OverlayPosition | undefined;
|
6
|
+
items: OverlayItem[];
|
7
|
+
};
|
8
|
+
events: {
|
9
|
+
[evt: string]: CustomEvent<any>;
|
10
|
+
};
|
11
|
+
slots: {};
|
12
|
+
};
|
13
|
+
export type OverlayLayerProps = typeof __propDef.props;
|
14
|
+
export type OverlayLayerEvents = typeof __propDef.events;
|
15
|
+
export type OverlayLayerSlots = typeof __propDef.slots;
|
16
|
+
export default class OverlayLayer extends SvelteComponent<OverlayLayerProps, OverlayLayerEvents, OverlayLayerSlots> {
|
17
|
+
constructor(options?: import("svelte").ComponentConstructorOptions<{
|
18
|
+
position?: OverlayPosition | undefined;
|
19
|
+
items: OverlayItem[];
|
20
|
+
}>);
|
21
|
+
}
|
22
|
+
export {};
|
@@ -78,7 +78,7 @@ let style = $derived.by(() => {
|
|
78
78
|
justify-content: center;
|
79
79
|
}
|
80
80
|
|
81
|
-
@media (max-width:
|
81
|
+
@media (max-width: 1280px) {
|
82
82
|
.Page .PaddingContainer {
|
83
83
|
padding-block-start: calc(var(--padBlockStart) / 2);
|
84
84
|
padding-block-end: calc(var(--padBlockEnd) / 2);
|
@@ -86,4 +86,12 @@ let style = $derived.by(() => {
|
|
86
86
|
padding-inline-end: calc(var(--padInlineEnd) / 2);
|
87
87
|
}
|
88
88
|
}
|
89
|
+
@media (max-width: 640px) {
|
90
|
+
.Page .PaddingContainer {
|
91
|
+
padding-block-start: calc(var(--padBlockStart) / 4);
|
92
|
+
padding-block-end: calc(var(--padBlockEnd) / 4);
|
93
|
+
padding-inline-start: calc(var(--padInlineStart) / 4);
|
94
|
+
padding-inline-end: calc(var(--padInlineEnd) / 4);
|
95
|
+
}
|
96
|
+
}
|
89
97
|
</style>
|
@@ -3,11 +3,8 @@ import { type Snippet } from "svelte";
|
|
3
3
|
declare const __propDef: {
|
4
4
|
props: {
|
5
5
|
children: Snippet;
|
6
|
-
/** Add padding to the container. Pass in either a boolean or an array of booleans to specify padding on the block and inline axes. */
|
7
6
|
pad?: string | boolean | [boolean, boolean] | [boolean, boolean, boolean] | [boolean, boolean, boolean, boolean] | [string, string] | [string, string, string] | [string, string, string, string] | undefined;
|
8
|
-
/** Center the content in the block direction. */
|
9
7
|
middle?: boolean | undefined;
|
10
|
-
/** Center the content in the inline direction. */
|
11
8
|
center?: boolean | undefined;
|
12
9
|
};
|
13
10
|
events: {
|
@@ -19,5 +16,13 @@ export type PageContentProps = typeof __propDef.props;
|
|
19
16
|
export type PageContentEvents = typeof __propDef.events;
|
20
17
|
export type PageContentSlots = typeof __propDef.slots;
|
21
18
|
export default class PageContent extends SvelteComponent<PageContentProps, PageContentEvents, PageContentSlots> {
|
19
|
+
constructor(options?: import("svelte").ComponentConstructorOptions<{
|
20
|
+
children: (this: void) => typeof import("svelte").SnippetReturn & {
|
21
|
+
_: "functions passed to {@render ...} tags must use the `Snippet` type imported from \"svelte\"";
|
22
|
+
};
|
23
|
+
pad?: string | boolean | [boolean, boolean] | [boolean, boolean, boolean] | [boolean, boolean, boolean, boolean] | [string, string] | [string, string, string] | [string, string, string, string] | undefined;
|
24
|
+
middle?: boolean | undefined;
|
25
|
+
center?: boolean | undefined;
|
26
|
+
}>);
|
22
27
|
}
|
23
28
|
export {};
|
package/dist/layout/Theme.svelte
CHANGED
@@ -31,6 +31,12 @@ setContext("theme", theme);
|
|
31
31
|
.Theme {
|
32
32
|
display: contents;
|
33
33
|
}
|
34
|
+
.Theme.light :global(:root) {
|
35
|
+
--scrollbar-color: var(--l-scrollbar-color);
|
36
|
+
}
|
37
|
+
.Theme.dark :global(:root) {
|
38
|
+
--scrollbar-color: var(--d-scrollbar-color);
|
39
|
+
}
|
34
40
|
.Theme.light {
|
35
41
|
--mix-target: var(--l-mix-target);
|
36
42
|
/* bg */
|
@@ -122,6 +128,7 @@ setContext("theme", theme);
|
|
122
128
|
--status-warn: var(--l-status-warn);
|
123
129
|
--status-info: var(--l-status-info);
|
124
130
|
--status-task: var(--l-status-task);
|
131
|
+
--scrollbar-color: var(--l-scrollbar-color);
|
125
132
|
}
|
126
133
|
.Theme.dark {
|
127
134
|
--mix-target: var(--d-mix-target);
|
@@ -216,5 +223,6 @@ setContext("theme", theme);
|
|
216
223
|
--status-warn: var(--d-status-warn);
|
217
224
|
--status-info: var(--d-status-info);
|
218
225
|
--status-task: var(--d-status-task);
|
226
|
+
--scrollbar-color: var(--d-scrollbar-color);
|
219
227
|
}
|
220
228
|
</style>
|
@@ -14,5 +14,11 @@ export type ThemeProps = typeof __propDef.props;
|
|
14
14
|
export type ThemeEvents = typeof __propDef.events;
|
15
15
|
export type ThemeSlots = typeof __propDef.slots;
|
16
16
|
export default class Theme extends SvelteComponent<ThemeProps, ThemeEvents, ThemeSlots> {
|
17
|
+
constructor(options?: import("svelte").ComponentConstructorOptions<{
|
18
|
+
theme?: "light" | "dark" | "invert" | undefined;
|
19
|
+
children: (this: void) => typeof import("svelte").SnippetReturn & {
|
20
|
+
_: "functions passed to {@render ...} tags must use the `Snippet` type imported from \"svelte\"";
|
21
|
+
};
|
22
|
+
}>);
|
17
23
|
}
|
18
24
|
export {};
|
@@ -13,5 +13,10 @@ export type UiContentProps = typeof __propDef.props;
|
|
13
13
|
export type UiContentEvents = typeof __propDef.events;
|
14
14
|
export type UiContentSlots = typeof __propDef.slots;
|
15
15
|
export default class UiContent extends SvelteComponent<UiContentProps, UiContentEvents, UiContentSlots> {
|
16
|
+
constructor(options?: import("svelte").ComponentConstructorOptions<{
|
17
|
+
children: (this: void) => typeof import("svelte").SnippetReturn & {
|
18
|
+
_: "functions passed to {@render ...} tags must use the `Snippet` type imported from \"svelte\"";
|
19
|
+
};
|
20
|
+
}>);
|
16
21
|
}
|
17
22
|
export {};
|
@@ -0,0 +1,34 @@
|
|
1
|
+
import type { ComponentType, Snippet } from "svelte";
|
2
|
+
export type OverlayPosition = "top left" | "top center" | "top right" | "bottom left" | "bottom center" | "bottom right" | "center" | "anchor";
|
3
|
+
export type TransitionOpts = {
|
4
|
+
y?: number;
|
5
|
+
x?: number;
|
6
|
+
duration?: number;
|
7
|
+
origin?: string;
|
8
|
+
easing?: (t: number) => number;
|
9
|
+
};
|
10
|
+
export type OverlayItem = {
|
11
|
+
id: string;
|
12
|
+
z?: number;
|
13
|
+
content: string | Snippet | ComponentType;
|
14
|
+
props?: Record<string, any>;
|
15
|
+
layer?: string;
|
16
|
+
anchor?: HTMLElement;
|
17
|
+
transition?: TransitionOpts;
|
18
|
+
position?: OverlayPosition;
|
19
|
+
sticky?: boolean;
|
20
|
+
stack?: string;
|
21
|
+
};
|
22
|
+
declare let overlays: {
|
23
|
+
topRight: OverlayItem[];
|
24
|
+
topLeft: OverlayItem[];
|
25
|
+
topCenter: OverlayItem[];
|
26
|
+
bottomRight: OverlayItem[];
|
27
|
+
bottomLeft: OverlayItem[];
|
28
|
+
bottomCenter: OverlayItem[];
|
29
|
+
center: OverlayItem[];
|
30
|
+
anchor: OverlayItem[];
|
31
|
+
};
|
32
|
+
declare function addOverlay(opts: OverlayItem): void;
|
33
|
+
declare function removeOverlay(id: string): void;
|
34
|
+
export { overlays, addOverlay, removeOverlay };
|
@@ -0,0 +1,44 @@
|
|
1
|
+
let overlays = $state({
|
2
|
+
topRight: [],
|
3
|
+
topLeft: [],
|
4
|
+
topCenter: [],
|
5
|
+
bottomRight: [],
|
6
|
+
bottomLeft: [],
|
7
|
+
bottomCenter: [],
|
8
|
+
center: [],
|
9
|
+
anchor: [],
|
10
|
+
});
|
11
|
+
function addOverlay(opts) {
|
12
|
+
switch (opts.position) {
|
13
|
+
case "top left":
|
14
|
+
overlays.topLeft.push(opts);
|
15
|
+
break;
|
16
|
+
case "top right":
|
17
|
+
overlays.topRight.push(opts);
|
18
|
+
break;
|
19
|
+
case "top center":
|
20
|
+
overlays.topCenter.push(opts);
|
21
|
+
break;
|
22
|
+
case "bottom left":
|
23
|
+
overlays.bottomLeft.push(opts);
|
24
|
+
break;
|
25
|
+
case "bottom right":
|
26
|
+
overlays.bottomRight.push(opts);
|
27
|
+
break;
|
28
|
+
case "bottom center":
|
29
|
+
overlays.bottomCenter.push(opts);
|
30
|
+
break;
|
31
|
+
case "center":
|
32
|
+
overlays.center.push(opts);
|
33
|
+
break;
|
34
|
+
default:
|
35
|
+
overlays.anchor.push(opts);
|
36
|
+
break;
|
37
|
+
}
|
38
|
+
}
|
39
|
+
function removeOverlay(id) {
|
40
|
+
for (const key in overlays) {
|
41
|
+
overlays[key] = overlays[key].filter((o) => o.id !== id);
|
42
|
+
}
|
43
|
+
}
|
44
|
+
export { overlays, addOverlay, removeOverlay };
|
@@ -3,17 +3,12 @@ import type { ComponentType } from "svelte";
|
|
3
3
|
import type { BreadcrumbItem } from "./MenuTypes.js";
|
4
4
|
declare const __propDef: {
|
5
5
|
props: {
|
6
|
-
/** The items to display in the breadcrumb. */
|
7
6
|
items: BreadcrumbItem[];
|
8
|
-
/** The separator to use between breadcrumb items. */
|
9
7
|
separator?: string | ((this: void) => typeof import("svelte").SnippetReturn & {
|
10
8
|
_: "functions passed to {@render ...} tags must use the `Snippet` type imported from \"svelte\"";
|
11
9
|
}) | ComponentType | undefined;
|
12
|
-
/** Contain the element in a box. */
|
13
10
|
contained?: boolean | undefined;
|
14
|
-
/** Make the element full width. */
|
15
11
|
fullWidth?: boolean | undefined;
|
16
|
-
/** Center the items in the element. */
|
17
12
|
center?: boolean | undefined;
|
18
13
|
};
|
19
14
|
events: {
|
@@ -25,5 +20,14 @@ export type BreadcrumbProps = typeof __propDef.props;
|
|
25
20
|
export type BreadcrumbEvents = typeof __propDef.events;
|
26
21
|
export type BreadcrumbSlots = typeof __propDef.slots;
|
27
22
|
export default class Breadcrumb extends SvelteComponent<BreadcrumbProps, BreadcrumbEvents, BreadcrumbSlots> {
|
23
|
+
constructor(options?: import("svelte").ComponentConstructorOptions<{
|
24
|
+
items: BreadcrumbItem[];
|
25
|
+
separator?: string | ((this: void) => typeof import("svelte").SnippetReturn & {
|
26
|
+
_: "functions passed to {@render ...} tags must use the `Snippet` type imported from \"svelte\"";
|
27
|
+
}) | ComponentType | undefined;
|
28
|
+
contained?: boolean | undefined;
|
29
|
+
fullWidth?: boolean | undefined;
|
30
|
+
center?: boolean | undefined;
|
31
|
+
}>);
|
28
32
|
}
|
29
33
|
export {};
|
package/dist/nav/Menu.svelte
CHANGED
@@ -1,10 +1,13 @@
|
|
1
|
-
<script lang="ts">import
|
1
|
+
<script lang="ts">import { BROWSER } from "esm-env";
|
2
|
+
import MenuItem from "./MenuItem.svelte";
|
2
3
|
import { isComponent } from "../utils/isSnippet.js";
|
3
4
|
import { createId } from "../utils/id.js";
|
4
5
|
import { slidefade } from "../utils/transitions.js";
|
5
6
|
import UiContent from "../layout/UIContent.svelte";
|
6
7
|
import { arrowNavigation, getNextFocusableElement, matchOnType } from "../utils/keyboard.svelte.js";
|
7
8
|
import StringOrComponentOrSnippet from "../utils/StringOrComponentOrSnippet.svelte";
|
9
|
+
import { findContainingBlock, getPossiblyContainedPosition } from "../utils/dom.js";
|
10
|
+
import Overlay from "../layout/Overlay.svelte";
|
8
11
|
let {
|
9
12
|
open = $bindable(false),
|
10
13
|
items,
|
@@ -14,10 +17,9 @@ let _open = $state(open);
|
|
14
17
|
let triggerEl = $state(null);
|
15
18
|
let contentEl = $state(null);
|
16
19
|
let menuEl = $state(null);
|
17
|
-
let
|
18
|
-
let
|
20
|
+
let currentIndex = $state(-1);
|
21
|
+
let keyboardHasFocus = $state(false);
|
19
22
|
const id = createId();
|
20
|
-
const fudge = 16;
|
21
23
|
$effect(() => {
|
22
24
|
if (_open) {
|
23
25
|
window.addEventListener("click", clickoutside);
|
@@ -30,40 +32,10 @@ $effect(() => {
|
|
30
32
|
function toggle() {
|
31
33
|
_open = !_open;
|
32
34
|
}
|
33
|
-
let
|
34
|
-
if (!
|
35
|
-
return
|
36
|
-
|
37
|
-
const elPos = contentEl.getBoundingClientRect();
|
38
|
-
const isOffRight = triggerPos.left + elPos.width > window.innerWidth - fudge;
|
39
|
-
const isOffBottom = triggerPos.bottom + elPos.height > window.innerHeight - fudge;
|
40
|
-
let text = "top left";
|
41
|
-
if (isOffRight)
|
42
|
-
text = text.replace("left", "right");
|
43
|
-
if (isOffBottom)
|
44
|
-
text = text.replace("top", "bottom");
|
45
|
-
return text;
|
46
|
-
});
|
47
|
-
let posLeft = $derived.by(() => {
|
48
|
-
if (!triggerEl || !contentEl)
|
49
|
-
return 0;
|
50
|
-
const triggerPos = triggerEl.getBoundingClientRect();
|
51
|
-
const isOffRight = triggerPos.left + menuWidth > window.innerWidth - fudge;
|
52
|
-
if (isOffRight) {
|
53
|
-
return triggerPos.left + triggerPos.width - menuWidth;
|
54
|
-
}
|
55
|
-
return triggerPos.left;
|
56
|
-
});
|
57
|
-
let posTop = $derived.by(() => {
|
58
|
-
if (!triggerEl || !contentEl)
|
59
|
-
return 0;
|
60
|
-
const triggerPos = triggerEl.getBoundingClientRect();
|
61
|
-
const scrollY = window.scrollY;
|
62
|
-
const isOffBottom = triggerPos.bottom + menuHeight > window.innerHeight - fudge;
|
63
|
-
if (isOffBottom) {
|
64
|
-
return triggerPos.top - menuHeight - triggerPos.height + scrollY;
|
65
|
-
}
|
66
|
-
return triggerPos.top + scrollY;
|
35
|
+
let scrollable = $derived.by(() => {
|
36
|
+
if (!contentEl)
|
37
|
+
return false;
|
38
|
+
return contentEl.scrollHeight > contentEl.clientHeight;
|
67
39
|
});
|
68
40
|
function onclick(e) {
|
69
41
|
e.preventDefault();
|
@@ -104,11 +76,13 @@ function onkeydown(e) {
|
|
104
76
|
e.preventDefault();
|
105
77
|
arrowNavigation(contentEl, "down");
|
106
78
|
matchOnType(contentEl, e);
|
79
|
+
keyboardHasFocus = true;
|
107
80
|
break;
|
108
81
|
case "ArrowUp":
|
109
82
|
e.preventDefault();
|
110
83
|
arrowNavigation(contentEl, "up");
|
111
84
|
matchOnType(contentEl, e);
|
85
|
+
keyboardHasFocus = true;
|
112
86
|
break;
|
113
87
|
case "Enter":
|
114
88
|
case "Space":
|
@@ -119,8 +93,14 @@ function onkeydown(e) {
|
|
119
93
|
matchOnType(contentEl, e);
|
120
94
|
}
|
121
95
|
}
|
96
|
+
function mouseover(e, item, index) {
|
97
|
+
if (item.type === "item") {
|
98
|
+
currentIndex = index;
|
99
|
+
}
|
100
|
+
}
|
122
101
|
</script>
|
123
102
|
|
103
|
+
|
124
104
|
<UiContent>
|
125
105
|
<div class="Menu" bind:this={menuEl}>
|
126
106
|
<div
|
@@ -135,22 +115,21 @@ function onkeydown(e) {
|
|
135
115
|
{@render trigger({ toggle: toggle, isOpen: _open })}
|
136
116
|
{/if}
|
137
117
|
</div>
|
138
|
-
{#if _open}
|
139
|
-
<
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
</div>
|
118
|
+
{#if _open && triggerEl}
|
119
|
+
<Overlay position="anchor" id="o-{id}" anchor={triggerEl} layer="menu">
|
120
|
+
<div {id}
|
121
|
+
class="MenuContent"
|
122
|
+
class:scrollable={scrollable}
|
123
|
+
role="menu"
|
124
|
+
bind:this={contentEl}
|
125
|
+
>
|
126
|
+
<ul>
|
127
|
+
{#each items as item, index}
|
128
|
+
<MenuItem {keyboardHasFocus} onmouseover={mouseover} item={item} {index} />
|
129
|
+
{/each}
|
130
|
+
</ul>
|
131
|
+
</div>
|
132
|
+
</Overlay>
|
154
133
|
{/if}
|
155
134
|
</div>
|
156
135
|
</UiContent>
|
@@ -166,9 +145,7 @@ function onkeydown(e) {
|
|
166
145
|
}
|
167
146
|
|
168
147
|
.MenuContent {
|
169
|
-
|
170
|
-
left: var(--left, 0);
|
171
|
-
top: var(--top, 0);
|
148
|
+
max-height: calc(50vh - 2rem);
|
172
149
|
margin: 0;
|
173
150
|
z-index: 1000;
|
174
151
|
margin: 0;
|
@@ -177,17 +154,21 @@ function onkeydown(e) {
|
|
177
154
|
box-shadow: 0 0.5rem 1rem var(--shadow);
|
178
155
|
background-color: var(--menu-bg);
|
179
156
|
width: var(--width, 25ch);
|
180
|
-
overflow: clip;
|
157
|
+
overflow-x: clip;
|
158
|
+
overflow-y: auto;
|
159
|
+
scrollbar-width: thin;
|
160
|
+
scrollbar-color: var(--scrollbar-color);
|
161
|
+
}
|
162
|
+
|
163
|
+
.MenuContent.scrollable {
|
164
|
+
border-top-right-radius: 0;
|
165
|
+
border-bottom-right-radius: 0;
|
181
166
|
}
|
182
167
|
|
183
168
|
.MenuContent:has(li:last-of-type[data-type="item"]) {
|
184
169
|
padding-block-end: 0.5rem;
|
185
170
|
}
|
186
171
|
|
187
|
-
nav[aria-expanded="true"] {
|
188
|
-
display: block;
|
189
|
-
}
|
190
|
-
|
191
172
|
ul {
|
192
173
|
margin: 0;
|
193
174
|
list-style: none;
|