@lavalogic/scoria 0.29.0 → 0.30.0
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/Components/Nav/CallbackButtonWithIcon.d.ts +23 -0
- package/dist/Components/Nav/CallbackButtonWithIcon.js +1 -0
- package/dist/Components/Nav/Dimensions.d.ts +4 -0
- package/dist/Components/Nav/Dimensions.js +5 -0
- package/dist/Components/Nav/Nav.svelte +216 -0
- package/dist/Components/Nav/Nav.svelte.d.ts +4 -0
- package/dist/Components/Nav/NavProps.d.ts +9 -0
- package/dist/Components/Nav/NavProps.js +1 -0
- package/dist/Components/Nav/NavServiceMenuList.svelte +55 -0
- package/dist/Components/Nav/NavServiceMenuList.svelte.d.ts +4 -0
- package/dist/Components/Nav/NavServiceMenuListProps.d.ts +7 -0
- package/dist/Components/Nav/NavServiceMenuListProps.js +1 -0
- package/dist/Components/Nav/NavTab.svelte +123 -0
- package/dist/Components/Nav/NavTab.svelte.d.ts +4 -0
- package/dist/Components/Nav/NavTabProps.d.ts +5 -0
- package/dist/Components/Nav/NavTabProps.js +1 -0
- package/dist/Components/Nav/ServiceMenuItem.svelte +154 -0
- package/dist/Components/Nav/ServiceMenuItem.svelte.d.ts +4 -0
- package/dist/Components/Nav/ServicesNav.svelte +91 -0
- package/dist/Components/Nav/ServicesNav.svelte.d.ts +4 -0
- package/dist/Components/Nav/ServicesNavProps.d.ts +6 -0
- package/dist/Components/Nav/ServicesNavProps.js +1 -0
- package/dist/Components/Nav/SideMenuItemProps.d.ts +11 -0
- package/dist/Components/Nav/SideMenuItemProps.js +5 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +11 -0
- package/package.json +1 -1
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Pathname } from '$app/types';
|
|
2
|
+
import type { IconProps } from '../IconProps.js';
|
|
3
|
+
export type CallbackButtonWithIcon = CallbackButtonWithPath | CallbackButtonWithCallback;
|
|
4
|
+
export interface NavButtonPartial {
|
|
5
|
+
iconName?: IconProps['name'];
|
|
6
|
+
label: string;
|
|
7
|
+
hoverLabel?: string;
|
|
8
|
+
subtitle?: string;
|
|
9
|
+
visible?: boolean | Promise<boolean>;
|
|
10
|
+
}
|
|
11
|
+
export type NavButton = NavButtonPartial & ({
|
|
12
|
+
path: Pathname;
|
|
13
|
+
} | {
|
|
14
|
+
callback: () => void;
|
|
15
|
+
});
|
|
16
|
+
export interface CallbackButtonWithPath extends NavButtonPartial {
|
|
17
|
+
path: Pathname;
|
|
18
|
+
callback?: never;
|
|
19
|
+
}
|
|
20
|
+
export interface CallbackButtonWithCallback extends NavButtonPartial {
|
|
21
|
+
path?: never;
|
|
22
|
+
callback: () => void;
|
|
23
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
<svelte:options runes />
|
|
2
|
+
|
|
3
|
+
<script lang="ts">
|
|
4
|
+
import { Icon, Size } from '@lavalogic/scoria';
|
|
5
|
+
import type { NavProps } from './NavProps.js';
|
|
6
|
+
import ServicesNav from './ServicesNav.svelte';
|
|
7
|
+
|
|
8
|
+
const { userName, extras, userDialog, serviceTabs, selectedServiceTab }: NavProps = $props();
|
|
9
|
+
|
|
10
|
+
const userIcon = $derived.by(() => {
|
|
11
|
+
const names = userName.split(' ');
|
|
12
|
+
|
|
13
|
+
const initials = names.map((it) => it.charAt(0)).slice(0, 2);
|
|
14
|
+
return initials;
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
const singleLetter = $derived(userIcon.length === 1);
|
|
18
|
+
const hasLetters = $derived(userIcon.length > 0);
|
|
19
|
+
|
|
20
|
+
let showServicesDialog = $state(false);
|
|
21
|
+
let showUserDialog = $state(false);
|
|
22
|
+
</script>
|
|
23
|
+
|
|
24
|
+
<nav class="main-navigation">
|
|
25
|
+
<span class="nav-link hoverable"> FloWMS </span>
|
|
26
|
+
<!-- HACK swap above for below when a decent landing page has been added -->
|
|
27
|
+
<!-- <a
|
|
28
|
+
href={resolve(Locations.Home)}
|
|
29
|
+
class="nav-link hoverable"
|
|
30
|
+
>
|
|
31
|
+
FloWMS
|
|
32
|
+
</a> -->
|
|
33
|
+
|
|
34
|
+
<div class="nav-link">
|
|
35
|
+
<button
|
|
36
|
+
onclick={() => {
|
|
37
|
+
showServicesDialog = !showServicesDialog;
|
|
38
|
+
}}
|
|
39
|
+
>
|
|
40
|
+
<div class="delimited">
|
|
41
|
+
<Icon
|
|
42
|
+
name="block-menu"
|
|
43
|
+
size={Size.Medium}
|
|
44
|
+
/>
|
|
45
|
+
<span>Services</span>
|
|
46
|
+
</div>
|
|
47
|
+
</button>
|
|
48
|
+
|
|
49
|
+
{#if showServicesDialog}
|
|
50
|
+
<ServicesNav
|
|
51
|
+
onclose={() => {
|
|
52
|
+
showServicesDialog = false;
|
|
53
|
+
}}
|
|
54
|
+
{serviceTabs}
|
|
55
|
+
{selectedServiceTab}
|
|
56
|
+
/>
|
|
57
|
+
{/if}
|
|
58
|
+
</div>
|
|
59
|
+
|
|
60
|
+
<div class="nav-search-container">
|
|
61
|
+
<!-- <TextInput
|
|
62
|
+
bind:value={searchParam}
|
|
63
|
+
placeholder="Search for anything"
|
|
64
|
+
leftIcon={{ name: 'search-1' }}
|
|
65
|
+
/> -->
|
|
66
|
+
</div>
|
|
67
|
+
{#if extras}
|
|
68
|
+
<div class="nav-link spaced selects">
|
|
69
|
+
{@render extras()}
|
|
70
|
+
</div>
|
|
71
|
+
{/if}
|
|
72
|
+
|
|
73
|
+
<div>
|
|
74
|
+
<button
|
|
75
|
+
onclick={() => {
|
|
76
|
+
showUserDialog = !showUserDialog;
|
|
77
|
+
}}
|
|
78
|
+
>
|
|
79
|
+
<span class="nav-link padded">
|
|
80
|
+
<span
|
|
81
|
+
class="user-icon"
|
|
82
|
+
class:visible={hasLetters}
|
|
83
|
+
class:single-letter={singleLetter}>{userIcon}</span
|
|
84
|
+
>
|
|
85
|
+
<span class="user-name">{userName}</span>
|
|
86
|
+
</span>
|
|
87
|
+
</button>
|
|
88
|
+
{#if showUserDialog}
|
|
89
|
+
{@render userDialog(() => {
|
|
90
|
+
showUserDialog = false;
|
|
91
|
+
})}
|
|
92
|
+
{/if}
|
|
93
|
+
</div>
|
|
94
|
+
</nav>
|
|
95
|
+
|
|
96
|
+
<style>div.overlay {
|
|
97
|
+
position: fixed;
|
|
98
|
+
top: calc(3.25rem + var(--env-border-height, 0));
|
|
99
|
+
bottom: 0;
|
|
100
|
+
left: 0;
|
|
101
|
+
right: 0;
|
|
102
|
+
z-index: 98;
|
|
103
|
+
background-color: rgba(0, 0, 0, 0.2);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
a.nav-link {
|
|
107
|
+
color: unset;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
div.user {
|
|
111
|
+
display: flex;
|
|
112
|
+
flex-flow: column nowrap;
|
|
113
|
+
gap: 0.75rem;
|
|
114
|
+
position: fixed;
|
|
115
|
+
border-radius: 4px;
|
|
116
|
+
background-color: #ffffff;
|
|
117
|
+
padding: 0.75rem;
|
|
118
|
+
top: calc(3.25rem + var(--env-border-height, 0));
|
|
119
|
+
right: 0.5rem;
|
|
120
|
+
z-index: 99;
|
|
121
|
+
box-shadow: 0px 0px 5px 0px rgba(0, 0, 0, 0.1333333333);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
nav {
|
|
125
|
+
position: relative;
|
|
126
|
+
grid-column-start: side-menu;
|
|
127
|
+
grid-column-end: end;
|
|
128
|
+
grid-row-start: nav-start;
|
|
129
|
+
grid-row-end: nav-end;
|
|
130
|
+
background-color: #ffffff;
|
|
131
|
+
display: flex;
|
|
132
|
+
flex-flow: row;
|
|
133
|
+
align-content: center;
|
|
134
|
+
justify-content: space-between;
|
|
135
|
+
border-bottom: solid 1px #cbd4da;
|
|
136
|
+
}
|
|
137
|
+
nav button {
|
|
138
|
+
background-color: #ffffff;
|
|
139
|
+
border: none;
|
|
140
|
+
height: 100%;
|
|
141
|
+
width: 100%;
|
|
142
|
+
}
|
|
143
|
+
nav button:hover {
|
|
144
|
+
background-color: rgba(47, 151, 186, 0.1);
|
|
145
|
+
cursor: pointer;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.spaced {
|
|
149
|
+
gap: 1rem;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.user-icon {
|
|
153
|
+
background-color: #dae0e5;
|
|
154
|
+
padding: 0.5rem;
|
|
155
|
+
border-radius: 17px;
|
|
156
|
+
opacity: 0;
|
|
157
|
+
font-weight: 600;
|
|
158
|
+
}
|
|
159
|
+
.user-icon.visible {
|
|
160
|
+
opacity: 1;
|
|
161
|
+
}
|
|
162
|
+
.user-icon.single-letter {
|
|
163
|
+
padding: 0.3rem 0.6rem;
|
|
164
|
+
border-radius: 14px;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
.main-navigation > *:nth-child(even) {
|
|
168
|
+
position: relative;
|
|
169
|
+
}
|
|
170
|
+
.main-navigation > *:nth-child(even)::before {
|
|
171
|
+
content: "";
|
|
172
|
+
height: 1.5rem;
|
|
173
|
+
border-left: solid 1px #e8e8e8;
|
|
174
|
+
border-radius: 10px;
|
|
175
|
+
left: 0;
|
|
176
|
+
position: absolute;
|
|
177
|
+
}
|
|
178
|
+
.main-navigation > *:nth-child(even)::after {
|
|
179
|
+
content: "";
|
|
180
|
+
height: 1.5rem;
|
|
181
|
+
border-right: solid 1px #e8e8e8;
|
|
182
|
+
border-radius: 10px;
|
|
183
|
+
position: absolute;
|
|
184
|
+
right: 0;
|
|
185
|
+
}
|
|
186
|
+
.main-navigation > *:nth-child(even).selects {
|
|
187
|
+
padding-left: 1px;
|
|
188
|
+
padding-right: 1px;
|
|
189
|
+
width: 32rem;
|
|
190
|
+
--sv-border: none;
|
|
191
|
+
--input-width: calc(50% - 0.5rem);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
.nav-link {
|
|
195
|
+
display: flex;
|
|
196
|
+
justify-content: center;
|
|
197
|
+
flex-flow: row;
|
|
198
|
+
align-items: center;
|
|
199
|
+
/* flex-grow: 1; */
|
|
200
|
+
min-width: 7.5rem;
|
|
201
|
+
transition: background-color 200ms ease 0s;
|
|
202
|
+
}
|
|
203
|
+
.nav-link.padded {
|
|
204
|
+
padding-left: 1rem;
|
|
205
|
+
padding-right: 1rem;
|
|
206
|
+
gap: 1rem;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
div.delimited {
|
|
210
|
+
padding: 0 0.5rem;
|
|
211
|
+
display: flex;
|
|
212
|
+
flex-flow: row;
|
|
213
|
+
align-items: center;
|
|
214
|
+
justify-content: center;
|
|
215
|
+
gap: 0.5rem;
|
|
216
|
+
}</style>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { TabOption } from '../../Types/Internal/TabOption.js';
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
export interface NavProps {
|
|
4
|
+
userName: string;
|
|
5
|
+
userDialog: Snippet<[closeUserDialog: () => void]>;
|
|
6
|
+
serviceTabs: Array<TabOption<unknown>>;
|
|
7
|
+
selectedServiceTab: TabOption<unknown> | undefined;
|
|
8
|
+
extras?: Snippet;
|
|
9
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
<svelte:options runes />
|
|
2
|
+
|
|
3
|
+
<script lang="ts">
|
|
4
|
+
import { Colour, Icon, Size } from '@lavalogic/scoria';
|
|
5
|
+
import type { NavServiceMenuListProps } from './NavServiceMenuListProps.js';
|
|
6
|
+
import ServiceMenuItem from './ServiceMenuItem.svelte';
|
|
7
|
+
|
|
8
|
+
const { title, items, icon }: NavServiceMenuListProps = $props();
|
|
9
|
+
</script>
|
|
10
|
+
|
|
11
|
+
<div class="wrapper">
|
|
12
|
+
<h3>
|
|
13
|
+
<div class="icon">
|
|
14
|
+
{#if icon}
|
|
15
|
+
<Icon
|
|
16
|
+
size={Size.Large}
|
|
17
|
+
colour={Colour['$font-primary']}
|
|
18
|
+
{...icon}
|
|
19
|
+
/>
|
|
20
|
+
{/if}
|
|
21
|
+
</div>
|
|
22
|
+
{title}
|
|
23
|
+
</h3>
|
|
24
|
+
|
|
25
|
+
<div class="items">
|
|
26
|
+
{#each items as { item } (item.label)}
|
|
27
|
+
<ServiceMenuItem {item} />
|
|
28
|
+
{/each}
|
|
29
|
+
</div>
|
|
30
|
+
</div>
|
|
31
|
+
|
|
32
|
+
<style>h3 {
|
|
33
|
+
font-size: 1.5rem;
|
|
34
|
+
display: flex;
|
|
35
|
+
flex-flow: row nowrap;
|
|
36
|
+
align-items: center;
|
|
37
|
+
color: #3a4952;
|
|
38
|
+
}
|
|
39
|
+
h3 .icon {
|
|
40
|
+
display: flex;
|
|
41
|
+
flex-flow: row nowrap;
|
|
42
|
+
align-items: center;
|
|
43
|
+
justify-content: center;
|
|
44
|
+
width: 3.4rem;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.wrapper {
|
|
48
|
+
display: flex;
|
|
49
|
+
flex-flow: column nowrap;
|
|
50
|
+
height: 100%;
|
|
51
|
+
}
|
|
52
|
+
.wrapper .items {
|
|
53
|
+
flex-shrink: 1;
|
|
54
|
+
overflow: auto;
|
|
55
|
+
}</style>
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { NavServiceMenuListProps } from './NavServiceMenuListProps.js';
|
|
2
|
+
declare const NavServiceMenuList: import("svelte").Component<NavServiceMenuListProps, {}, "">;
|
|
3
|
+
type NavServiceMenuList = ReturnType<typeof NavServiceMenuList>;
|
|
4
|
+
export default NavServiceMenuList;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
<svelte:options runes />
|
|
2
|
+
|
|
3
|
+
<script lang="ts">
|
|
4
|
+
import { devCatch } from '../../Helpers/Helpers.svelte.js';
|
|
5
|
+
import { Colour } from '../../scss/colours.js';
|
|
6
|
+
import { Size } from '../../Types/Internal/Size.js';
|
|
7
|
+
import Icon from '../Icon.svelte';
|
|
8
|
+
import type { NavTabProps } from './NavTabProps.js';
|
|
9
|
+
|
|
10
|
+
let { option, selected }: NavTabProps = $props();
|
|
11
|
+
|
|
12
|
+
let disabled = $state(true);
|
|
13
|
+
let visible = $state(true);
|
|
14
|
+
let isValid = $state(true);
|
|
15
|
+
|
|
16
|
+
$effect(() => {
|
|
17
|
+
if (typeof option.isValid === 'boolean') {
|
|
18
|
+
isValid = option.isValid;
|
|
19
|
+
} else {
|
|
20
|
+
option.isValid
|
|
21
|
+
?.then((v) => {
|
|
22
|
+
isValid = v;
|
|
23
|
+
})
|
|
24
|
+
.catch(devCatch);
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
$effect(() => {
|
|
28
|
+
if (typeof option.disabled === 'boolean') {
|
|
29
|
+
disabled = option.disabled;
|
|
30
|
+
} else if (option.disabled == undefined) {
|
|
31
|
+
disabled = false;
|
|
32
|
+
} else {
|
|
33
|
+
option.disabled
|
|
34
|
+
.then((v) => {
|
|
35
|
+
disabled = v;
|
|
36
|
+
})
|
|
37
|
+
.catch(devCatch);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
$effect(() => {
|
|
42
|
+
if (typeof option.visible === 'boolean') {
|
|
43
|
+
visible = option.visible;
|
|
44
|
+
} else if (option.visible == undefined) {
|
|
45
|
+
visible = true;
|
|
46
|
+
} else {
|
|
47
|
+
option.visible
|
|
48
|
+
.then((v) => {
|
|
49
|
+
visible = v;
|
|
50
|
+
})
|
|
51
|
+
.catch(devCatch);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
</script>
|
|
55
|
+
|
|
56
|
+
<button
|
|
57
|
+
class:selected
|
|
58
|
+
onclick={() => {
|
|
59
|
+
option.onclick();
|
|
60
|
+
}}
|
|
61
|
+
type="button"
|
|
62
|
+
class="tab"
|
|
63
|
+
{disabled}
|
|
64
|
+
class:invalid={!isValid}
|
|
65
|
+
class:hidden={!visible}
|
|
66
|
+
>
|
|
67
|
+
<Icon
|
|
68
|
+
name="nothing"
|
|
69
|
+
colour={Colour['$brand-primary']}
|
|
70
|
+
size={Size.MediumLarge}
|
|
71
|
+
{...option.iconLeft}
|
|
72
|
+
/>
|
|
73
|
+
|
|
74
|
+
{option.label}
|
|
75
|
+
{#if option.isValid === false}
|
|
76
|
+
<Icon
|
|
77
|
+
name="error"
|
|
78
|
+
colour={Colour['$error-border']}
|
|
79
|
+
size={Size.MediumLarge}
|
|
80
|
+
/>
|
|
81
|
+
{:else}
|
|
82
|
+
<Icon
|
|
83
|
+
name="nothing"
|
|
84
|
+
colour={Colour['$brand-primary']}
|
|
85
|
+
size={Size.MediumLarge}
|
|
86
|
+
{...option.iconRight}
|
|
87
|
+
/>
|
|
88
|
+
{/if}
|
|
89
|
+
</button>
|
|
90
|
+
|
|
91
|
+
<style>button.tab.hidden {
|
|
92
|
+
display: none !important;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.tab {
|
|
96
|
+
color: #3a4952;
|
|
97
|
+
cursor: pointer;
|
|
98
|
+
font-size: 1rem;
|
|
99
|
+
font-weight: 500;
|
|
100
|
+
padding: 1.5rem;
|
|
101
|
+
background-color: transparent;
|
|
102
|
+
border: none;
|
|
103
|
+
transition: border-color ease-out 0.15s 0s, background-color ease-out 0.15s 0s, color ease-out 0.15s 0s, fill ease-out 0.15s 0s;
|
|
104
|
+
text-align: start;
|
|
105
|
+
display: flex;
|
|
106
|
+
flex-flow: row nowrap;
|
|
107
|
+
align-items: center;
|
|
108
|
+
width: 100%;
|
|
109
|
+
justify-content: start;
|
|
110
|
+
gap: 0.5rem;
|
|
111
|
+
}
|
|
112
|
+
.tab:first-child {
|
|
113
|
+
border-radius: 4px 0 0 0;
|
|
114
|
+
}
|
|
115
|
+
.tab.selected {
|
|
116
|
+
color: #259fc7;
|
|
117
|
+
}
|
|
118
|
+
.tab:hover, .tab.selected:hover, .tab.invalid:hover {
|
|
119
|
+
background-color: #f4f7f9;
|
|
120
|
+
}
|
|
121
|
+
.tab.invalid {
|
|
122
|
+
color: #aa1414;
|
|
123
|
+
}</style>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
<svelte:options runes />
|
|
2
|
+
|
|
3
|
+
<script lang="ts">
|
|
4
|
+
import { dev } from '$app/environment';
|
|
5
|
+
import { resolve } from '$app/paths';
|
|
6
|
+
import { page } from '$app/state';
|
|
7
|
+
import { devCatch, Icon, Size } from '@lavalogic/scoria';
|
|
8
|
+
import { Dimensions } from './Dimensions.js';
|
|
9
|
+
import type { SideMenuItemProps } from './SideMenuItemProps.js';
|
|
10
|
+
|
|
11
|
+
const { item, selected: maybeSelected }: SideMenuItemProps = $props();
|
|
12
|
+
|
|
13
|
+
const selected = $derived(maybeSelected ?? (item.path ? page.url.pathname == item.path : false));
|
|
14
|
+
|
|
15
|
+
let visible = $state(true);
|
|
16
|
+
|
|
17
|
+
$effect(() => {
|
|
18
|
+
if (typeof item.visible === 'boolean') {
|
|
19
|
+
visible = item.visible;
|
|
20
|
+
} else if (item.visible == undefined) {
|
|
21
|
+
visible = true;
|
|
22
|
+
} else {
|
|
23
|
+
item.visible
|
|
24
|
+
.then((v) => {
|
|
25
|
+
visible = v;
|
|
26
|
+
})
|
|
27
|
+
.catch(devCatch);
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
</script>
|
|
31
|
+
|
|
32
|
+
{#if item.callback}
|
|
33
|
+
<button
|
|
34
|
+
onclick={() => {
|
|
35
|
+
item.callback();
|
|
36
|
+
}}
|
|
37
|
+
style="--iconWidth: {Dimensions.smallPanelWidth}rem"
|
|
38
|
+
class:selected
|
|
39
|
+
class:hidden={!visible}
|
|
40
|
+
>
|
|
41
|
+
<!-- <div class="selected-marker"></div> -->
|
|
42
|
+
<div class="icon-wrapper">
|
|
43
|
+
{#if item.iconName}
|
|
44
|
+
<Icon
|
|
45
|
+
name={item.iconName}
|
|
46
|
+
size={Size.Medium}
|
|
47
|
+
label={item.label}
|
|
48
|
+
/>
|
|
49
|
+
{/if}
|
|
50
|
+
</div>
|
|
51
|
+
<div class="spacer"></div>
|
|
52
|
+
<div class="text">
|
|
53
|
+
<div class="label">{item.label}</div>
|
|
54
|
+
{#if item.subtitle}
|
|
55
|
+
<div class="subtitle">{item.subtitle}</div>
|
|
56
|
+
{/if}
|
|
57
|
+
</div></button
|
|
58
|
+
>
|
|
59
|
+
{:else if item.path}
|
|
60
|
+
<!-- FIXME why is this type cast required? -->
|
|
61
|
+
<a
|
|
62
|
+
href={resolve(item.path as '/')}
|
|
63
|
+
style="--iconWidth: {Dimensions.smallPanelWidth}rem"
|
|
64
|
+
class:selected
|
|
65
|
+
class:hidden={!visible}
|
|
66
|
+
>
|
|
67
|
+
<!-- <div class="selected-marker"></div> -->
|
|
68
|
+
<div class="icon-wrapper">
|
|
69
|
+
{#if item.iconName}
|
|
70
|
+
<Icon
|
|
71
|
+
name={item.iconName}
|
|
72
|
+
size={Size.Medium}
|
|
73
|
+
label={item.label}
|
|
74
|
+
/>
|
|
75
|
+
{/if}
|
|
76
|
+
</div>
|
|
77
|
+
<div class="spacer"></div>
|
|
78
|
+
<div class="text">
|
|
79
|
+
<div class="label">{item.label}</div>
|
|
80
|
+
{#if item.subtitle}
|
|
81
|
+
<div class="subtitle">{item.subtitle}</div>
|
|
82
|
+
{/if}
|
|
83
|
+
</div></a
|
|
84
|
+
>
|
|
85
|
+
{:else if dev}
|
|
86
|
+
Error: Callback had neither callback nor path
|
|
87
|
+
{/if}
|
|
88
|
+
|
|
89
|
+
<style>button.hidden,
|
|
90
|
+
a.hidden {
|
|
91
|
+
display: none !important;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
button,
|
|
95
|
+
a {
|
|
96
|
+
font-size: 1rem;
|
|
97
|
+
background-color: transparent;
|
|
98
|
+
border: none;
|
|
99
|
+
cursor: pointer;
|
|
100
|
+
padding: 0.8rem 0;
|
|
101
|
+
display: flex;
|
|
102
|
+
flex-flow: row nowrap;
|
|
103
|
+
justify-content: flex-start;
|
|
104
|
+
align-items: center;
|
|
105
|
+
position: relative;
|
|
106
|
+
color: #455661;
|
|
107
|
+
fill: #455661;
|
|
108
|
+
box-sizing: border-box;
|
|
109
|
+
}
|
|
110
|
+
button div,
|
|
111
|
+
a div {
|
|
112
|
+
user-select: none;
|
|
113
|
+
}
|
|
114
|
+
button .text,
|
|
115
|
+
a .text {
|
|
116
|
+
display: flex;
|
|
117
|
+
flex-flow: column nowrap;
|
|
118
|
+
gap: 0.75rem;
|
|
119
|
+
}
|
|
120
|
+
button .text .label,
|
|
121
|
+
a .text .label {
|
|
122
|
+
font-weight: 500;
|
|
123
|
+
}
|
|
124
|
+
button .subtitle,
|
|
125
|
+
a .subtitle {
|
|
126
|
+
font-size: 0.875rem;
|
|
127
|
+
color: #9da9b1;
|
|
128
|
+
}
|
|
129
|
+
button,
|
|
130
|
+
a {
|
|
131
|
+
transition: border-color ease-out 0.15s 0s, background-color ease-out 0.15s 0s, color ease-out 0.15s 0s, fill ease-out 0.15s 0s;
|
|
132
|
+
}
|
|
133
|
+
button .icon-wrapper,
|
|
134
|
+
a .icon-wrapper {
|
|
135
|
+
width: var(--iconWidth);
|
|
136
|
+
display: flex;
|
|
137
|
+
flex-flow: row nowrap;
|
|
138
|
+
align-items: center;
|
|
139
|
+
justify-content: center;
|
|
140
|
+
box-sizing: border-box;
|
|
141
|
+
}
|
|
142
|
+
button.selected, button:hover,
|
|
143
|
+
a.selected,
|
|
144
|
+
a:hover {
|
|
145
|
+
color: #259fc7;
|
|
146
|
+
}
|
|
147
|
+
button:hover,
|
|
148
|
+
a:hover {
|
|
149
|
+
background-color: #f4f7f9;
|
|
150
|
+
}
|
|
151
|
+
button .spacer,
|
|
152
|
+
a .spacer {
|
|
153
|
+
width: 0.5rem;
|
|
154
|
+
}</style>
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
<svelte:options runes />
|
|
2
|
+
|
|
3
|
+
<script lang="ts">
|
|
4
|
+
import { HorizontalTabGroup, type TabOption } from '@lavalogic/scoria';
|
|
5
|
+
import NavTab from './NavTab.svelte';
|
|
6
|
+
import type { ServicesNavProps } from './ServicesNavProps.js';
|
|
7
|
+
|
|
8
|
+
const { onclose, serviceTabs, selectedServiceTab }: ServicesNavProps = $props();
|
|
9
|
+
|
|
10
|
+
let servicesDialogRef: HTMLDivElement | undefined = $state.raw();
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<div
|
|
14
|
+
bind:this={servicesDialogRef}
|
|
15
|
+
role="dialog"
|
|
16
|
+
tabindex="0"
|
|
17
|
+
class="overlay"
|
|
18
|
+
onclick={(e) => {
|
|
19
|
+
e.stopPropagation();
|
|
20
|
+
if (servicesDialogRef === e.target) {
|
|
21
|
+
onclose();
|
|
22
|
+
}
|
|
23
|
+
}}
|
|
24
|
+
onkeydown={(e) => {
|
|
25
|
+
if (e.key === 'Escape') {
|
|
26
|
+
onclose();
|
|
27
|
+
}
|
|
28
|
+
}}
|
|
29
|
+
>
|
|
30
|
+
<div
|
|
31
|
+
role="navigation"
|
|
32
|
+
class="services"
|
|
33
|
+
>
|
|
34
|
+
<HorizontalTabGroup
|
|
35
|
+
name="nav-services"
|
|
36
|
+
tabs={serviceTabs}
|
|
37
|
+
selected={selectedServiceTab}
|
|
38
|
+
tabComponent={navTab}
|
|
39
|
+
noRadius
|
|
40
|
+
grow
|
|
41
|
+
/>
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
|
|
45
|
+
{#snippet navTab(option: TabOption<unknown>, selected: boolean)}
|
|
46
|
+
<NavTab
|
|
47
|
+
{option}
|
|
48
|
+
{selected}
|
|
49
|
+
/>
|
|
50
|
+
{/snippet}
|
|
51
|
+
|
|
52
|
+
<style>div.overlay {
|
|
53
|
+
position: fixed;
|
|
54
|
+
top: calc(3.25rem + var(--env-border-height, 0));
|
|
55
|
+
bottom: 0;
|
|
56
|
+
left: 0;
|
|
57
|
+
right: 0;
|
|
58
|
+
z-index: 98;
|
|
59
|
+
background-color: rgba(0, 0, 0, 0.2);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
div.services {
|
|
63
|
+
position: fixed;
|
|
64
|
+
border-radius: 4px;
|
|
65
|
+
background-color: #ffffff;
|
|
66
|
+
top: calc(3.25rem + var(--env-border-height, 0));
|
|
67
|
+
left: 7.5rem;
|
|
68
|
+
z-index: 99;
|
|
69
|
+
box-shadow: 0px 0px 5px 0px rgba(0, 0, 0, 0.1333333333);
|
|
70
|
+
padding: 0;
|
|
71
|
+
display: flex;
|
|
72
|
+
flex-flow: row nowrap;
|
|
73
|
+
align-items: stretch;
|
|
74
|
+
justify-content: stretch;
|
|
75
|
+
height: min(710px, 80%);
|
|
76
|
+
width: 800px;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
div.wrapper {
|
|
80
|
+
display: flex;
|
|
81
|
+
flex-flow: row wrap;
|
|
82
|
+
width: 600px;
|
|
83
|
+
gap: 1.5rem;
|
|
84
|
+
--input-width: 100%;
|
|
85
|
+
}
|
|
86
|
+
div.wrapper .row {
|
|
87
|
+
width: 100%;
|
|
88
|
+
gap: 1.5rem;
|
|
89
|
+
display: flex;
|
|
90
|
+
flex-flow: row nowrap;
|
|
91
|
+
}</style>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { CallbackButtonWithIcon } from './CallbackButtonWithIcon.js';
|
|
2
|
+
export declare const SideMenuItemVariant: {
|
|
3
|
+
readonly Nav: "Nav";
|
|
4
|
+
readonly Tab: "Tab";
|
|
5
|
+
};
|
|
6
|
+
export type SideMenuItemVariant = (typeof SideMenuItemVariant)[keyof typeof SideMenuItemVariant];
|
|
7
|
+
export interface SideMenuItemProps {
|
|
8
|
+
item: CallbackButtonWithIcon;
|
|
9
|
+
selected?: boolean;
|
|
10
|
+
variant?: SideMenuItemVariant;
|
|
11
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -58,6 +58,17 @@ export type { MultiSelectCommonProps } from './Components/MultiSelectCommonProps
|
|
|
58
58
|
export type { MultiSelectProps } from './Components/MultiSelectProps.js';
|
|
59
59
|
export type { MultiSelectPropsAsObject } from './Components/MultiSelectPropsAsObject.js';
|
|
60
60
|
export type { MultiSelectPropsAsPrimitive } from './Components/MultiSelectPropsAsPrimitive.js';
|
|
61
|
+
export { type CallbackButtonWithCallback, type CallbackButtonWithIcon, type CallbackButtonWithPath, type NavButton, type NavButtonPartial, } from './Components/Nav/CallbackButtonWithIcon.js';
|
|
62
|
+
export { Dimensions } from './Components/Nav/Dimensions.js';
|
|
63
|
+
export { default as Nav } from './Components/Nav/Nav.svelte';
|
|
64
|
+
export { type NavProps } from './Components/Nav/NavProps.js';
|
|
65
|
+
export { default as NavServiceMenuList } from './Components/Nav/NavServiceMenuList.svelte';
|
|
66
|
+
export { type NavServiceMenuListProps } from './Components/Nav/NavServiceMenuListProps.js';
|
|
67
|
+
export { default as NavTab } from './Components/Nav/NavTab.svelte';
|
|
68
|
+
export { default as ServiceMenuItem } from './Components/Nav/ServiceMenuItem.svelte';
|
|
69
|
+
export { default as ServicesNav } from './Components/Nav/ServicesNav.svelte';
|
|
70
|
+
export { type ServicesNavProps } from './Components/Nav/ServicesNavProps.js';
|
|
71
|
+
export { SideMenuItemVariant, type SideMenuItemProps, } from './Components/Nav/SideMenuItemProps.js';
|
|
61
72
|
export { default as NumberInput } from './Components/NumberInput.svelte';
|
|
62
73
|
export type { NumberInputProps } from './Components/NumberInputProps.js';
|
|
63
74
|
export { type PartialIconProps } from './Components/PartialIconProps.js';
|
package/dist/index.js
CHANGED
|
@@ -38,6 +38,17 @@ export { default as LoadingModal } from './Components/LoadingModal.svelte';
|
|
|
38
38
|
export { default as LoadingOverlay } from './Components/LoadingOverlay.svelte';
|
|
39
39
|
export { default as Modal } from './Components/Modal.svelte';
|
|
40
40
|
export { default as MultiSelect } from './Components/MultiSelect.svelte';
|
|
41
|
+
export {} from './Components/Nav/CallbackButtonWithIcon.js';
|
|
42
|
+
export { Dimensions } from './Components/Nav/Dimensions.js';
|
|
43
|
+
export { default as Nav } from './Components/Nav/Nav.svelte';
|
|
44
|
+
export {} from './Components/Nav/NavProps.js';
|
|
45
|
+
export { default as NavServiceMenuList } from './Components/Nav/NavServiceMenuList.svelte';
|
|
46
|
+
export {} from './Components/Nav/NavServiceMenuListProps.js';
|
|
47
|
+
export { default as NavTab } from './Components/Nav/NavTab.svelte';
|
|
48
|
+
export { default as ServiceMenuItem } from './Components/Nav/ServiceMenuItem.svelte';
|
|
49
|
+
export { default as ServicesNav } from './Components/Nav/ServicesNav.svelte';
|
|
50
|
+
export {} from './Components/Nav/ServicesNavProps.js';
|
|
51
|
+
export { SideMenuItemVariant, } from './Components/Nav/SideMenuItemProps.js';
|
|
41
52
|
export { default as NumberInput } from './Components/NumberInput.svelte';
|
|
42
53
|
export {} from './Components/PartialIconProps.js';
|
|
43
54
|
export { default as PasswordInput } from './Components/PasswordInput.svelte';
|