lutra 0.1.0 → 0.1.5
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/Avatar.svelte +105 -0
- package/dist/components/Avatar.svelte.d.ts +14 -0
- package/dist/components/Close.svelte +76 -0
- package/dist/components/Close.svelte.d.ts +7 -0
- package/dist/components/ContextTip.svelte +41 -0
- package/dist/components/ContextTip.svelte.d.ts +7 -0
- package/dist/components/Icon.svelte +62 -0
- package/dist/components/Icon.svelte.d.ts +8 -0
- package/dist/components/IconButton.svelte +120 -0
- package/dist/components/IconButton.svelte.d.ts +16 -0
- package/dist/components/Image.svelte +172 -0
- package/dist/components/Image.svelte.d.ts +56 -0
- package/dist/components/Indicator.svelte +387 -0
- package/dist/components/Indicator.svelte.d.ts +12 -0
- package/dist/components/Inset.svelte +23 -0
- package/dist/components/Inset.svelte.d.ts +7 -0
- package/dist/components/Layout.svelte +2 -1
- package/dist/components/MenuDropdown.svelte +195 -0
- package/dist/components/MenuDropdown.svelte.d.ts +16 -0
- package/dist/components/MenuItem.svelte +159 -0
- package/dist/components/MenuItem.svelte.d.ts +11 -0
- package/dist/components/MenuItemContent.svelte +25 -0
- package/dist/components/MenuItemContent.svelte.d.ts +10 -0
- package/dist/components/MenuTypes.d.ts +79 -0
- package/dist/components/MenuTypes.js +1 -0
- package/dist/components/Modal.svelte +149 -0
- package/dist/components/Modal.svelte.d.ts +16 -0
- package/dist/components/Notification.svelte +115 -0
- package/dist/components/Notification.svelte.d.ts +12 -0
- package/dist/components/Overlay.svelte +31 -0
- package/dist/components/Overlay.svelte.d.ts +14 -0
- package/dist/components/OverlayContainer.svelte +31 -0
- package/dist/components/OverlayContainer.svelte.d.ts +18 -0
- package/dist/components/OverlayLayer.svelte +168 -0
- package/dist/components/OverlayLayer.svelte.d.ts +8 -0
- package/dist/components/PageContent.svelte +4 -82
- package/dist/components/PageContent.svelte.d.ts +0 -31
- package/dist/components/TabbedContent.svelte +74 -0
- package/dist/components/TabbedContent.svelte.d.ts +11 -0
- package/dist/components/TabbedContentItem.svelte +33 -0
- package/dist/components/TabbedContentItem.svelte.d.ts +10 -0
- package/dist/components/Table.svelte +41 -0
- package/dist/components/Table.svelte.d.ts +13 -0
- package/dist/components/Tabs.svelte +216 -0
- package/dist/components/Tabs.svelte.d.ts +20 -0
- package/dist/components/Tag.svelte +120 -0
- package/dist/components/Tag.svelte.d.ts +21 -0
- package/dist/components/Theme.svelte +32 -14
- package/dist/components/Tooltip.svelte +8 -8
- package/dist/components/UIContent.svelte +19 -0
- package/dist/components/UIContent.svelte.d.ts +7 -0
- package/dist/components/index.d.ts +28 -0
- package/dist/components/index.js +29 -0
- package/dist/components/notifications.svelte.d.ts +21 -0
- package/dist/components/notifications.svelte.js +30 -0
- package/dist/components/overlays.svelte.d.ts +36 -0
- package/dist/components/overlays.svelte.js +44 -0
- package/dist/css/1-props.css +389 -724
- package/dist/css/2-base.css +257 -123
- package/dist/css/3-typo.css +74 -34
- package/dist/css/4-layout.css +364 -1
- package/dist/css/5-media.css +106 -11
- package/dist/css/lutra.css +2 -1
- package/dist/css/themes/DefaultTheme.css +209 -0
- package/dist/form/Button.svelte +58 -0
- package/dist/form/Button.svelte.d.ts +15 -0
- package/dist/form/Datepicker.svelte +311 -0
- package/dist/form/Datepicker.svelte.d.ts +9 -0
- package/dist/form/FieldContent.svelte +178 -0
- package/dist/form/FieldContent.svelte.d.ts +21 -0
- package/dist/form/FieldError.svelte +24 -0
- package/dist/form/FieldError.svelte.d.ts +7 -0
- package/dist/form/Fieldset.svelte +103 -0
- package/dist/form/Fieldset.svelte.d.ts +20 -0
- package/dist/form/Form.svelte +220 -0
- package/dist/form/Form.svelte.d.ts +38 -0
- package/dist/form/FormActions.svelte +80 -0
- package/dist/form/FormActions.svelte.d.ts +9 -0
- package/dist/form/FormSection.svelte +96 -0
- package/dist/form/FormSection.svelte.d.ts +9 -0
- package/dist/form/ImageUpload.svelte +299 -0
- package/dist/form/ImageUpload.svelte.d.ts +20 -0
- package/dist/form/Input.svelte +444 -0
- package/dist/form/Input.svelte.d.ts +108 -0
- package/dist/form/InputLength.svelte +42 -0
- package/dist/form/InputLength.svelte.d.ts +9 -0
- package/dist/form/Label.svelte +88 -0
- package/dist/form/Label.svelte.d.ts +16 -0
- package/dist/form/LogoUpload.svelte +115 -0
- package/dist/form/LogoUpload.svelte.d.ts +18 -0
- package/dist/form/Select.svelte +186 -0
- package/dist/form/Select.svelte.d.ts +59 -0
- package/dist/form/Textarea.svelte +265 -0
- package/dist/form/Textarea.svelte.d.ts +95 -0
- package/dist/form/Toggle.svelte +4 -0
- package/dist/form/Toggle.svelte.d.ts +18 -0
- package/dist/form/client.svelte.d.ts +45 -0
- package/dist/form/client.svelte.js +102 -0
- package/dist/form/form.d.ts +55 -0
- package/dist/form/form.js +345 -0
- package/dist/form/index.d.ts +17 -0
- package/dist/form/index.js +17 -0
- package/dist/form/types.d.ts +55 -0
- package/dist/form/types.js +1 -0
- package/dist/icons/IconAlert.svelte +3 -0
- package/dist/icons/IconAlert.svelte.d.ts +26 -0
- package/dist/icons/IconCopy.svelte +3 -0
- package/dist/icons/IconCopy.svelte.d.ts +26 -0
- package/dist/icons/IconDone.svelte +3 -0
- package/dist/icons/IconDone.svelte.d.ts +26 -0
- package/dist/icons/IconError.svelte +3 -0
- package/dist/icons/IconError.svelte.d.ts +26 -0
- package/dist/icons/IconHelp.svelte +3 -0
- package/dist/icons/IconHelp.svelte.d.ts +26 -0
- package/dist/icons/IconHide.svelte +3 -0
- package/dist/icons/IconHide.svelte.d.ts +26 -0
- package/dist/icons/IconInfo.svelte +3 -0
- package/dist/icons/IconInfo.svelte.d.ts +26 -0
- package/dist/icons/IconLink.svelte +3 -0
- package/dist/icons/IconLink.svelte.d.ts +26 -0
- package/dist/icons/IconMenuBurger.svelte +3 -0
- package/dist/icons/IconMenuBurger.svelte.d.ts +26 -0
- package/dist/icons/IconMenuDots.svelte +3 -0
- package/dist/icons/IconMenuDots.svelte.d.ts +26 -0
- package/dist/icons/IconSearch.svelte +3 -0
- package/dist/icons/IconSearch.svelte.d.ts +26 -0
- package/dist/icons/IconShow.svelte +3 -0
- package/dist/icons/IconShow.svelte.d.ts +26 -0
- package/dist/icons/IconSuccess.svelte +3 -0
- package/dist/icons/IconSuccess.svelte.d.ts +26 -0
- package/dist/icons/IconWarning.svelte +3 -0
- package/dist/icons/IconWarning.svelte.d.ts +26 -0
- package/dist/icons/index.d.ts +14 -0
- package/dist/icons/index.js +14 -0
- package/dist/index.d.ts +3 -5
- package/dist/index.js +3 -5
- package/dist/util/StringOrComponent.svelte +20 -0
- package/dist/util/StringOrComponent.svelte.d.ts +8 -0
- package/dist/util/StringOrSnippet.svelte +16 -0
- package/dist/util/StringOrSnippet.svelte.d.ts +8 -0
- package/dist/util/attr.d.ts +5 -0
- package/dist/util/attr.js +21 -0
- package/dist/util/color.d.ts +51 -0
- package/dist/util/color.js +97 -0
- package/dist/util/dom.d.ts +15 -0
- package/dist/util/dom.js +73 -0
- package/dist/util/keyboard.svelte.d.ts +22 -0
- package/dist/util/keyboard.svelte.js +161 -0
- package/dist/util/locale.d.ts +1 -0
- package/dist/util/locale.js +47 -0
- package/dist/util/settings.d.ts +4 -0
- package/dist/util/settings.js +1 -0
- package/package.json +20 -11
- package/dist/css/0-layers.css +0 -1
@@ -0,0 +1,216 @@
|
|
1
|
+
<script lang="ts">
|
2
|
+
import { browser } from "$app/environment";
|
3
|
+
import { page } from "$app/stores";
|
4
|
+
import type { TabItem } from "./MenuTypes.js";
|
5
|
+
import { onMount } from "svelte";
|
6
|
+
|
7
|
+
/**
|
8
|
+
* @description
|
9
|
+
* A tabbed navigation menu used to navigate between pages or sections of content.
|
10
|
+
* Clicking a tab will change the URL or trigger a JavaScript event.
|
11
|
+
* @example
|
12
|
+
* <Tabs contained rounded items={[
|
13
|
+
* {
|
14
|
+
* label: 'Tab 1',
|
15
|
+
* href: '#tab1',
|
16
|
+
* },
|
17
|
+
* {
|
18
|
+
* label: 'Tab 2',
|
19
|
+
* href: '#tab2',
|
20
|
+
* },
|
21
|
+
* {
|
22
|
+
* label: 'Tab 3',
|
23
|
+
* href: '#tab3',
|
24
|
+
* },
|
25
|
+
* ]} />
|
26
|
+
*/
|
27
|
+
let {
|
28
|
+
items,
|
29
|
+
underline,
|
30
|
+
contained,
|
31
|
+
rounded,
|
32
|
+
selected = $bindable<{ label: string, href?: string, index: number } | null>(null)
|
33
|
+
}: {
|
34
|
+
/** Tab items to display. */
|
35
|
+
items: TabItem[];
|
36
|
+
/** Underline the active tab. The underline will slide to the selected tab unless reduced motion is enabled. */
|
37
|
+
underline?: boolean;
|
38
|
+
/** Contain the element in a box. */
|
39
|
+
contained?: boolean;
|
40
|
+
/** Round the corners of the element if contained. */
|
41
|
+
rounded?: boolean;
|
42
|
+
/** The index of the selected tab (bindable). */
|
43
|
+
selected?: { label: string, href?: string, index: number } | null;
|
44
|
+
} = $props();
|
45
|
+
|
46
|
+
const id = $state(`Tabs-${Math.random().toString(36).slice(2)}`);
|
47
|
+
|
48
|
+
const itemsWithId = $derived(items.map((item, index) => {
|
49
|
+
return { ...item, id: `Tab-${id}-${index}` };
|
50
|
+
}));
|
51
|
+
|
52
|
+
function onClickButton(event: MouseEvent, item: TabItem, index: number) {
|
53
|
+
if(item.onclick) {
|
54
|
+
item.onclick(event, item, index);
|
55
|
+
}
|
56
|
+
selected = { label: item.label, href: item.href, index };
|
57
|
+
}
|
58
|
+
|
59
|
+
let activeIndex = $derived.by(() => {
|
60
|
+
return items.findIndex((item, index) => {
|
61
|
+
console.log(item.href);
|
62
|
+
if(!item.href) return false;
|
63
|
+
// check if href ends in *
|
64
|
+
if(item.href.endsWith('*')) {
|
65
|
+
return $page.url.pathname.startsWith(item.href.slice(0, -1));
|
66
|
+
} else {
|
67
|
+
return $page.url.pathname.endsWith(item.href);
|
68
|
+
}
|
69
|
+
})
|
70
|
+
});
|
71
|
+
|
72
|
+
$effect(() => {
|
73
|
+
console.log(activeIndex);
|
74
|
+
});
|
75
|
+
|
76
|
+
let loaded = $state(false);
|
77
|
+
|
78
|
+
onMount(async () => {
|
79
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
80
|
+
loaded = true;
|
81
|
+
});
|
82
|
+
|
83
|
+
let underlineStyle = $derived.by(() => {
|
84
|
+
if(browser && underline && loaded) {
|
85
|
+
let active = items[activeIndex];
|
86
|
+
let activeElement: HTMLElement | null = document.querySelector(`#${id} li[data-index="${activeIndex}"]`);
|
87
|
+
if(activeElement) {
|
88
|
+
return `
|
89
|
+
opacity: 1;
|
90
|
+
width: ${activeElement.clientWidth}px;
|
91
|
+
transform: translateX(${activeElement.offsetLeft}px)
|
92
|
+
`
|
93
|
+
}
|
94
|
+
}
|
95
|
+
return 'height: 0;';
|
96
|
+
|
97
|
+
});
|
98
|
+
|
99
|
+
function removePossibleStar(href: string) {
|
100
|
+
return href.endsWith('*') ? href.slice(0, -1) : href;
|
101
|
+
}
|
102
|
+
|
103
|
+
</script>
|
104
|
+
|
105
|
+
<svelte:window onresize={() => { loaded = false; loaded = true; }} />
|
106
|
+
|
107
|
+
<nav class="Tabs" {id} class:contained class:rounded>
|
108
|
+
<menu>
|
109
|
+
{#each items as item, index}
|
110
|
+
<li data-index={index} aria-current={item.active || activeIndex === index ? 'page' : undefined}>
|
111
|
+
{#if item.href}
|
112
|
+
<a draggable="false" href={removePossibleStar(item.href)}>
|
113
|
+
{item.label}
|
114
|
+
</a>
|
115
|
+
{:else if item.onclick}
|
116
|
+
<button type="button" draggable="false" onclick={(event) => onClickButton(event, item, index)}>
|
117
|
+
{item.label}
|
118
|
+
</button>
|
119
|
+
{/if}
|
120
|
+
</li>
|
121
|
+
{/each}
|
122
|
+
<div class="Underline" style={underlineStyle}></div>
|
123
|
+
</menu>
|
124
|
+
</nav>
|
125
|
+
|
126
|
+
<style>
|
127
|
+
.Tabs {
|
128
|
+
display: flex;
|
129
|
+
z-index: 2;
|
130
|
+
background: var(--menu-bg);
|
131
|
+
overflow: clip;
|
132
|
+
}
|
133
|
+
.Tabs.contained {
|
134
|
+
border: var(--menu-border);
|
135
|
+
}
|
136
|
+
.Tabs.rounded {
|
137
|
+
border-radius: var(--border-radius);
|
138
|
+
}
|
139
|
+
menu, li {
|
140
|
+
list-style: none;
|
141
|
+
margin: 0;
|
142
|
+
}
|
143
|
+
menu {
|
144
|
+
position: relative;
|
145
|
+
display: flex;
|
146
|
+
flex-direction: row;
|
147
|
+
flex-wrap: nowrap;
|
148
|
+
align-items: center;
|
149
|
+
justify-content: flex-start;
|
150
|
+
padding: 0;
|
151
|
+
gap: var(--gap, 1rem);
|
152
|
+
inline-size: 100%;
|
153
|
+
border-block-end: var(--menu-border);
|
154
|
+
}
|
155
|
+
.Tabs.contained menu {
|
156
|
+
gap: 0;
|
157
|
+
flex-grow: 1;
|
158
|
+
justify-content: stretch;
|
159
|
+
border-block-end: 0;
|
160
|
+
}
|
161
|
+
a,
|
162
|
+
button {
|
163
|
+
display: block;
|
164
|
+
padding: var(--padding, 0.75rem 0.5rem);
|
165
|
+
color: var(--menu-text);
|
166
|
+
transition: all var(--menu-trans);
|
167
|
+
font-weight: 500;
|
168
|
+
font-size: var(--font-size, 0.9em);
|
169
|
+
letter-spacing: -0.05ch;
|
170
|
+
background: transparent;
|
171
|
+
border: none;
|
172
|
+
border-block-end: 2px solid transparent;
|
173
|
+
cursor: pointer;
|
174
|
+
text-decoration: none;
|
175
|
+
}
|
176
|
+
menu li:first-child > * {
|
177
|
+
padding-inline-start: 0;
|
178
|
+
}
|
179
|
+
.Tabs.contained li {
|
180
|
+
flex-grow: 1;
|
181
|
+
border-inline-end: var(--menu-border);
|
182
|
+
flex-basis: auto;
|
183
|
+
}
|
184
|
+
.Tabs.contained menu li:last-of-type {
|
185
|
+
border-inline-end: 0;
|
186
|
+
}
|
187
|
+
.Tabs.contained a,
|
188
|
+
.Tabs.contained button {
|
189
|
+
flex-grow: 1;
|
190
|
+
inline-size: 100%;
|
191
|
+
text-align: center;
|
192
|
+
padding-block-start: calc(0.75rem + 3px);
|
193
|
+
color: var(--menu-text);
|
194
|
+
font-weight: 600;
|
195
|
+
}
|
196
|
+
a:hover,
|
197
|
+
button:hover {
|
198
|
+
color: color-mix(in hsl shorter hue, var(--menu-text) var(--mix-amount), var(--mix-target));
|
199
|
+
background-color: color-mix(in hsl shorter hue, var(--menu-bg) var(--mix-amount), var(--mix-target));
|
200
|
+
}
|
201
|
+
li[aria-current="page"] a,
|
202
|
+
li[aria-current="page"] button {
|
203
|
+
background: var(--menu-bg-active);
|
204
|
+
color: var(--menu-text-active);
|
205
|
+
opacity: 1;
|
206
|
+
}
|
207
|
+
.Underline {
|
208
|
+
height: 2px;
|
209
|
+
background-color: var(--menu-text-active);
|
210
|
+
position: absolute;
|
211
|
+
bottom: 0;
|
212
|
+
left: 0;
|
213
|
+
transition: all 0.2s ease-out;
|
214
|
+
opacity: 0;
|
215
|
+
}
|
216
|
+
</style>
|
@@ -0,0 +1,20 @@
|
|
1
|
+
import type { TabItem } from "./MenuTypes.js";
|
2
|
+
type $$ComponentProps = {
|
3
|
+
/** Tab items to display. */
|
4
|
+
items: TabItem[];
|
5
|
+
/** Underline the active tab. The underline will slide to the selected tab unless reduced motion is enabled. */
|
6
|
+
underline?: boolean;
|
7
|
+
/** Contain the element in a box. */
|
8
|
+
contained?: boolean;
|
9
|
+
/** Round the corners of the element if contained. */
|
10
|
+
rounded?: boolean;
|
11
|
+
/** The index of the selected tab (bindable). */
|
12
|
+
selected?: {
|
13
|
+
label: string;
|
14
|
+
href?: string;
|
15
|
+
index: number;
|
16
|
+
} | null;
|
17
|
+
};
|
18
|
+
declare const Tabs: import("svelte").Component<$$ComponentProps, {}, "selected">;
|
19
|
+
type Tabs = ReturnType<typeof Tabs>;
|
20
|
+
export default Tabs;
|
@@ -0,0 +1,120 @@
|
|
1
|
+
<script lang="ts">
|
2
|
+
import type { Snippet } from "svelte";
|
3
|
+
import { isStatusColor } from '../util/color.js';
|
4
|
+
import type { StatusColorOrString } from '../util/color.js';
|
5
|
+
/**
|
6
|
+
* @description
|
7
|
+
* A tag is a small piece of information that can be used to categorize or label an item. You can pick between `rounded`, `pill` or `rectangle` shapes. The font size and padding are relative to the parent element by default.
|
8
|
+
* Changing the font size without changing the padding will cause padding to be scaled with the font size. This can be changed by setting the `--padding` CSS prop.
|
9
|
+
* You can pass a `href` to make the tag a link, or an `onclick` function to make it a button. If you pass a `href`, you can also pass a `target` to specify the target of the link.
|
10
|
+
* @cssprop --font-size - The font size of the tag. (Default: 1em)
|
11
|
+
* @cssprop --font-weight - The font weight of the tag. (Default: 500)
|
12
|
+
* @cssprop --padding - The padding of the tag. (Default: 0.25em 0.5em)
|
13
|
+
* @cssprop --vertical-align - The vertical alignment of the tag. (Default: baseline)
|
14
|
+
* @example
|
15
|
+
* <Tag href="https://google.com" target="_blank" --font-size="1.5rem" color="alert">Tag with a link</Tag>
|
16
|
+
* <Tag --padding="0.5em 1em" color="warn" onclick={() => { alert("Hi!"); }}>Warning</Tag>
|
17
|
+
* <Tag color="ok">Status OK</Tag>
|
18
|
+
* <Tag color="info">Informational</Tag>
|
19
|
+
* <p>Here's an inline <Tag code>code tag</Tag> in some text</p>
|
20
|
+
*/
|
21
|
+
let {
|
22
|
+
code,
|
23
|
+
color = "default",
|
24
|
+
shape = "pill",
|
25
|
+
onclick,
|
26
|
+
href,
|
27
|
+
target,
|
28
|
+
children,
|
29
|
+
}: {
|
30
|
+
/** Use a mono-spaced font for the tag. */
|
31
|
+
code?: boolean;
|
32
|
+
/** The color of the tag. */
|
33
|
+
color?: StatusColorOrString;
|
34
|
+
/** The shape of the tag. */
|
35
|
+
shape?: "rounded" | "pill" | "rectangle";
|
36
|
+
/** A function to run when the tag is clicked. */
|
37
|
+
onclick?: (event: MouseEvent) => void;
|
38
|
+
/** A URL to link to. */
|
39
|
+
href?: string;
|
40
|
+
/** The target of the link. */
|
41
|
+
target?: string;
|
42
|
+
/** The content of the tag. */
|
43
|
+
children: Snippet;
|
44
|
+
} = $props();
|
45
|
+
let isSet = $derived(color ? isStatusColor(color) : true);
|
46
|
+
</script>
|
47
|
+
|
48
|
+
{#snippet content()}
|
49
|
+
<em>
|
50
|
+
{@render children()}
|
51
|
+
</em>
|
52
|
+
{/snippet}
|
53
|
+
|
54
|
+
{#if href}
|
55
|
+
<a {href} {target} class:code class="Tag Link {shape}" style="--bgColor: {!isSet ? color : 'var(--status-'+color+'-background)'}; --textColor: {!isSet ? color : 'var(--status-'+color+'-color)'};" onclick={onclick}>
|
56
|
+
{@render content()}
|
57
|
+
</a>
|
58
|
+
{:else if onclick}
|
59
|
+
<button type="button" class:code class="Tag {shape}" style="--bgColor: {!isSet ? color : 'var(--status-'+color+'-background)'}; --textColor: {!isSet ? color : 'var(--status-'+color+'-color)'};" onclick={onclick}>
|
60
|
+
{@render content()}
|
61
|
+
</button>
|
62
|
+
{:else}
|
63
|
+
<span class:code class="Tag {shape}" style="--bgColor: {!isSet ? color : 'var(--status-'+color+'-background)'}; --textColor: {!isSet ? color : 'var(--status-'+color+'-color)'};">
|
64
|
+
{@render content()}
|
65
|
+
</span>
|
66
|
+
{/if}
|
67
|
+
|
68
|
+
<style>
|
69
|
+
.Tag {
|
70
|
+
display: inline-flex;
|
71
|
+
padding: var(--padding, 0.5em 0.5em);
|
72
|
+
font-weight: var(--font-weight, 500);
|
73
|
+
background: var(--bgColor);
|
74
|
+
border-radius: var(--border-radius);
|
75
|
+
font-size: var(--font-size, 0.85em);
|
76
|
+
vertical-align: var(--vertical-align, baseline);
|
77
|
+
border: 1px solid color-mix(in srgb, var(--bgColor) 95%, var(--mix-target, black) 5%);
|
78
|
+
}
|
79
|
+
.Tag.rectangle {
|
80
|
+
border-radius: 0;
|
81
|
+
}
|
82
|
+
.Tag.pill {
|
83
|
+
border-radius: 99999px;
|
84
|
+
}
|
85
|
+
.Tag.code {
|
86
|
+
font-family: var(--font-family-mono);
|
87
|
+
padding: var(--padding, 0.15em 0.35em);
|
88
|
+
font-size: 1em;
|
89
|
+
line-height: 1em;
|
90
|
+
background: var(--bg, var(--bg-subtle));
|
91
|
+
color: var(--textColor);
|
92
|
+
border: var(--border, var(--border-subtle));
|
93
|
+
}
|
94
|
+
em {
|
95
|
+
font-style: normal;
|
96
|
+
left: 50%;
|
97
|
+
text-box: trim-both cap alphabetic;
|
98
|
+
position: relative;
|
99
|
+
display: block;
|
100
|
+
transform: translateX(-50%);
|
101
|
+
}
|
102
|
+
a:hover, button:hover {
|
103
|
+
--bgColor2: color-mix(in srgb, var(--bgColor) 87%, white 13%);
|
104
|
+
background-color: var(--bgColor2);
|
105
|
+
cursor: pointer;
|
106
|
+
}
|
107
|
+
@media (prefers-contrast: more) {
|
108
|
+
.Tag {
|
109
|
+
border: 1px solid color-mix(in srgb, var(--bgColor) 50%, var(--mix-target, black) 50%);
|
110
|
+
}
|
111
|
+
em {
|
112
|
+
opacity: 0.99;
|
113
|
+
}
|
114
|
+
}
|
115
|
+
@media (prefers-contrast: less) {
|
116
|
+
em {
|
117
|
+
opacity: 0.65;
|
118
|
+
}
|
119
|
+
}
|
120
|
+
</style>
|
@@ -0,0 +1,21 @@
|
|
1
|
+
import type { Snippet } from "svelte";
|
2
|
+
import type { StatusColorOrString } from '../util/color.js';
|
3
|
+
type $$ComponentProps = {
|
4
|
+
/** Use a mono-spaced font for the tag. */
|
5
|
+
code?: boolean;
|
6
|
+
/** The color of the tag. */
|
7
|
+
color?: StatusColorOrString;
|
8
|
+
/** The shape of the tag. */
|
9
|
+
shape?: "rounded" | "pill" | "rectangle";
|
10
|
+
/** A function to run when the tag is clicked. */
|
11
|
+
onclick?: (event: MouseEvent) => void;
|
12
|
+
/** A URL to link to. */
|
13
|
+
href?: string;
|
14
|
+
/** The target of the link. */
|
15
|
+
target?: string;
|
16
|
+
/** The content of the tag. */
|
17
|
+
children: Snippet;
|
18
|
+
};
|
19
|
+
declare const Tag: import("svelte").Component<$$ComponentProps, {}, "">;
|
20
|
+
type Tag = ReturnType<typeof Tag>;
|
21
|
+
export default Tag;
|
@@ -1,6 +1,6 @@
|
|
1
1
|
<script lang="ts">
|
2
2
|
import { browser } from "$app/environment";
|
3
|
-
import { setContext, type Snippet } from "svelte";
|
3
|
+
import { onMount, setContext, type Snippet } from "svelte";
|
4
4
|
import { defaultConfig, type LutraConfig, type LutraRootConfig } from "../config.js";
|
5
5
|
import { getContextItem, LutraContext, type LutraTheme } from "../types.js";
|
6
6
|
|
@@ -32,6 +32,8 @@
|
|
32
32
|
// Retrieve existing configuration from context if available
|
33
33
|
const existingConfig = getContextItem(LutraContext.Config);
|
34
34
|
|
35
|
+
let _theme = $state(theme);
|
36
|
+
|
35
37
|
// If no config is provided, use the existing one from context or default to the defaultConfig
|
36
38
|
if(!config) {
|
37
39
|
config = existingConfig || defaultConfig;
|
@@ -41,7 +43,7 @@
|
|
41
43
|
setContext(LutraContext.Config, config);
|
42
44
|
|
43
45
|
// Retrieve existing theme from context if available
|
44
|
-
|
46
|
+
let existingTheme = getContextItem(LutraContext.Theme);
|
45
47
|
|
46
48
|
function getTheme() {
|
47
49
|
if(browser) {
|
@@ -53,11 +55,25 @@
|
|
53
55
|
|
54
56
|
// If no theme is provided, determine it from localStorage or system preferences
|
55
57
|
if(!theme) {
|
56
|
-
|
58
|
+
_theme = existingTheme || getTheme();
|
57
59
|
} else if(theme === 'invert') {
|
58
|
-
|
60
|
+
_theme = (existingTheme || getTheme()) === 'light' ? 'dark' : 'light';
|
59
61
|
}
|
60
62
|
|
63
|
+
onMount(() => {
|
64
|
+
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
65
|
+
mediaQuery.addEventListener('change', (e) => {
|
66
|
+
if(browser && root && !theme && !existingTheme && !localStorage.getItem('lutra.theme')) {
|
67
|
+
_theme = e.matches ? 'dark' : 'light';
|
68
|
+
} else if(root && theme === "invert") {
|
69
|
+
_theme = existingTheme === 'light' ? 'dark' : 'light';
|
70
|
+
existingTheme = getContextItem(LutraContext.Theme);
|
71
|
+
} else if(root && !theme) {
|
72
|
+
_theme = existingTheme || getTheme();
|
73
|
+
}
|
74
|
+
});
|
75
|
+
});
|
76
|
+
|
61
77
|
// Set the theme in the context for child components to access
|
62
78
|
setContext(LutraContext.Theme, theme);
|
63
79
|
</script>
|
@@ -68,20 +84,22 @@
|
|
68
84
|
{/if}
|
69
85
|
</svelte:head>
|
70
86
|
|
71
|
-
<div class="Theme{
|
87
|
+
<div class="Theme {_theme}" class:root>
|
72
88
|
{@render children()}
|
73
89
|
</div>
|
74
90
|
|
75
91
|
<style>
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
92
|
+
@layer base {
|
93
|
+
.Theme {
|
94
|
+
display: contents;
|
95
|
+
color-scheme: light dark;
|
96
|
+
}
|
97
|
+
.Theme.light {
|
98
|
+
color-scheme: only light;
|
99
|
+
}
|
100
|
+
.Theme.dark {
|
101
|
+
color-scheme: only dark;
|
102
|
+
}
|
85
103
|
}
|
86
104
|
</style>
|
87
105
|
|
@@ -45,7 +45,7 @@
|
|
45
45
|
</span>
|
46
46
|
<svg width="12" height="8" viewBox="0 0 12 8" version="1.1">
|
47
47
|
<g transform="matrix(0.75,0,0,1,0,0)">
|
48
|
-
<path d="M16,0L0,0L8,8L16,0Z" fill="var(--
|
48
|
+
<path d="M16,0L0,0L8,8L16,0Z" fill="var(--tooltip-background)"/>
|
49
49
|
</g>
|
50
50
|
</svg>
|
51
51
|
</Theme>
|
@@ -71,22 +71,22 @@
|
|
71
71
|
display: flex;
|
72
72
|
flex-direction: column;
|
73
73
|
align-items: center;
|
74
|
-
filter: drop-shadow(0px 2px 5px var(--
|
74
|
+
filter: drop-shadow(0px 2px 5px var(--tooltip-shadow-color));
|
75
75
|
opacity: 0;
|
76
76
|
pointer-events: none;
|
77
77
|
}
|
78
78
|
.TooltipContent {
|
79
|
-
background-color: var(--
|
79
|
+
background-color: var(--tooltip-background);
|
80
80
|
padding: 0.35rem 0.5rem;
|
81
|
-
border-radius:
|
82
|
-
border-top: var(--border-
|
83
|
-
border-left: var(--border-
|
84
|
-
border-right: var(--border-
|
81
|
+
border-radius: var(--tooltip-border-radius);
|
82
|
+
border-top: var(--tooltip-border-size);
|
83
|
+
border-left: var(--tooltip-border-size);
|
84
|
+
border-right: var(--tooltip-border-size);
|
85
85
|
display: block;
|
86
86
|
font-size: max(0.75rem, 11px);
|
87
87
|
line-height: 1.35;
|
88
88
|
font-weight: 500;
|
89
|
-
color: var(--
|
89
|
+
color: var(--tooltip-color);
|
90
90
|
max-width: clamp(25ch, 100%, 25ch);
|
91
91
|
width: max-content;
|
92
92
|
}
|
@@ -0,0 +1,19 @@
|
|
1
|
+
<script lang="ts">
|
2
|
+
import type { Snippet } from "svelte";
|
3
|
+
let {
|
4
|
+
children,
|
5
|
+
}: {
|
6
|
+
children: Snippet;
|
7
|
+
} = $props();
|
8
|
+
</script>
|
9
|
+
|
10
|
+
<div class="UIContent">
|
11
|
+
{@render children()}
|
12
|
+
</div>
|
13
|
+
|
14
|
+
<style>
|
15
|
+
.UIContent {
|
16
|
+
display: contents;
|
17
|
+
--isPage: 0;
|
18
|
+
}
|
19
|
+
</style>
|
@@ -0,0 +1,28 @@
|
|
1
|
+
export { default as AspectRatio } from './AspectRatio.svelte';
|
2
|
+
export { default as Avatar } from './Avatar.svelte';
|
3
|
+
export { default as Close } from './Close.svelte';
|
4
|
+
export { default as ContextTip } from './ContextTip.svelte';
|
5
|
+
export { default as Dialog } from './Dialog.svelte';
|
6
|
+
export { default as Icon } from './Icon.svelte';
|
7
|
+
export { default as IconButton } from './IconButton.svelte';
|
8
|
+
export { default as Image } from './Image.svelte';
|
9
|
+
export { default as Indicator } from './Indicator.svelte';
|
10
|
+
export { default as Inset } from './Inset.svelte';
|
11
|
+
export { default as Layout } from './Layout.svelte';
|
12
|
+
export { default as MenuDropdown } from './MenuDropdown.svelte';
|
13
|
+
export { default as MenuItem } from './MenuItem.svelte';
|
14
|
+
export { default as MenuItemContent } from './MenuItemContent.svelte';
|
15
|
+
export { default as Modal } from './Modal.svelte';
|
16
|
+
export { default as Notification } from './Notification.svelte';
|
17
|
+
export { default as Overlay } from './Overlay.svelte';
|
18
|
+
export { default as OverlayContainer } from './OverlayContainer.svelte';
|
19
|
+
export { default as OverlayLayer } from './OverlayLayer.svelte';
|
20
|
+
export { default as PageContent } from './PageContent.svelte';
|
21
|
+
export { default as Tag } from './Tag.svelte';
|
22
|
+
export { default as Table } from './Table.svelte';
|
23
|
+
export { default as Theme } from './Theme.svelte';
|
24
|
+
export { default as Tooltip } from './Tooltip.svelte';
|
25
|
+
export { default as UIContent } from './UIContent.svelte';
|
26
|
+
export * from './MenuTypes.js';
|
27
|
+
export * from './notifications.svelte.js';
|
28
|
+
export * from './overlays.svelte.js';
|
@@ -0,0 +1,29 @@
|
|
1
|
+
export { default as AspectRatio } from './AspectRatio.svelte';
|
2
|
+
export { default as Avatar } from './Avatar.svelte';
|
3
|
+
export { default as Close } from './Close.svelte';
|
4
|
+
export { default as ContextTip } from './ContextTip.svelte';
|
5
|
+
export { default as Dialog } from './Dialog.svelte';
|
6
|
+
export { default as Icon } from './Icon.svelte';
|
7
|
+
export { default as IconButton } from './IconButton.svelte';
|
8
|
+
export { default as Image } from './Image.svelte';
|
9
|
+
export { default as Indicator } from './Indicator.svelte';
|
10
|
+
export { default as Inset } from './Inset.svelte';
|
11
|
+
export { default as Layout } from './Layout.svelte';
|
12
|
+
export { default as MenuDropdown } from './MenuDropdown.svelte';
|
13
|
+
export { default as MenuItem } from './MenuItem.svelte';
|
14
|
+
export { default as MenuItemContent } from './MenuItemContent.svelte';
|
15
|
+
export { default as Modal } from './Modal.svelte';
|
16
|
+
export { default as Notification } from './Notification.svelte';
|
17
|
+
export { default as Overlay } from './Overlay.svelte';
|
18
|
+
export { default as OverlayContainer } from './OverlayContainer.svelte';
|
19
|
+
export { default as OverlayLayer } from './OverlayLayer.svelte';
|
20
|
+
export { default as PageContent } from './PageContent.svelte';
|
21
|
+
export { default as Tag } from './Tag.svelte';
|
22
|
+
export { default as Table } from './Table.svelte';
|
23
|
+
export { default as Theme } from './Theme.svelte';
|
24
|
+
export { default as Tooltip } from './Tooltip.svelte';
|
25
|
+
export { default as UIContent } from './UIContent.svelte';
|
26
|
+
// Export TypeScript files and stores
|
27
|
+
export * from './MenuTypes.js';
|
28
|
+
export * from './notifications.svelte.js';
|
29
|
+
export * from './overlays.svelte.js';
|
@@ -0,0 +1,21 @@
|
|
1
|
+
import { type OverlayPosition } from "./overlays.svelte.js";
|
2
|
+
import type { Component, Snippet } from "svelte";
|
3
|
+
/**
|
4
|
+
* Add a notification to the screen.
|
5
|
+
* @param {object} opts - The options for the notification.
|
6
|
+
* @param {string} opts.content - The content of the notification.
|
7
|
+
* @param {OverlayPosition} opts.position - The position of the notification.
|
8
|
+
* @param {Snippet | Component} opts.icon - The icon of the notification.
|
9
|
+
* @param {string} opts.title - The title of the notification.
|
10
|
+
* @param {string} opts.stack - Group notifications together by named stacks.
|
11
|
+
* @param {number} opts.autoClose - The time in milliseconds before the notification closes.
|
12
|
+
* @returns {void}
|
13
|
+
*/
|
14
|
+
export declare function addNotification(opts: {
|
15
|
+
content: string;
|
16
|
+
position?: OverlayPosition;
|
17
|
+
icon?: Snippet | Component;
|
18
|
+
title?: string;
|
19
|
+
stack?: string;
|
20
|
+
autoClose?: number;
|
21
|
+
}): void;
|
@@ -0,0 +1,30 @@
|
|
1
|
+
import { addOverlay, removeOverlay } from "./overlays.svelte.js";
|
2
|
+
import NotificationSvelte from "./Notification.svelte";
|
3
|
+
/**
|
4
|
+
* Add a notification to the screen.
|
5
|
+
* @param {object} opts - The options for the notification.
|
6
|
+
* @param {string} opts.content - The content of the notification.
|
7
|
+
* @param {OverlayPosition} opts.position - The position of the notification.
|
8
|
+
* @param {Snippet | Component} opts.icon - The icon of the notification.
|
9
|
+
* @param {string} opts.title - The title of the notification.
|
10
|
+
* @param {string} opts.stack - Group notifications together by named stacks.
|
11
|
+
* @param {number} opts.autoClose - The time in milliseconds before the notification closes.
|
12
|
+
* @returns {void}
|
13
|
+
*/
|
14
|
+
export function addNotification(opts) {
|
15
|
+
const id = crypto.randomUUID();
|
16
|
+
addOverlay({
|
17
|
+
id,
|
18
|
+
z: 100,
|
19
|
+
component: NotificationSvelte,
|
20
|
+
props: { id, ...opts },
|
21
|
+
stack: opts.stack,
|
22
|
+
position: opts.position || "bottom right",
|
23
|
+
layer: "notifications",
|
24
|
+
});
|
25
|
+
if (opts.autoClose) {
|
26
|
+
setTimeout(() => {
|
27
|
+
removeOverlay(id);
|
28
|
+
}, opts.autoClose);
|
29
|
+
}
|
30
|
+
}
|
@@ -0,0 +1,36 @@
|
|
1
|
+
import type { Component, 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
|
+
text?: string;
|
14
|
+
snippet?: Snippet;
|
15
|
+
component?: Component;
|
16
|
+
props?: any;
|
17
|
+
layer?: string;
|
18
|
+
anchor?: HTMLElement;
|
19
|
+
transition?: TransitionOpts;
|
20
|
+
position?: OverlayPosition;
|
21
|
+
sticky?: boolean;
|
22
|
+
stack?: string;
|
23
|
+
};
|
24
|
+
declare let overlays: {
|
25
|
+
topRight: OverlayItem[];
|
26
|
+
topLeft: OverlayItem[];
|
27
|
+
topCenter: OverlayItem[];
|
28
|
+
bottomRight: OverlayItem[];
|
29
|
+
bottomLeft: OverlayItem[];
|
30
|
+
bottomCenter: OverlayItem[];
|
31
|
+
center: OverlayItem[];
|
32
|
+
anchor: OverlayItem[];
|
33
|
+
};
|
34
|
+
declare function addOverlay(opts: OverlayItem): void;
|
35
|
+
declare function removeOverlay(id: string): void;
|
36
|
+
export { overlays, addOverlay, removeOverlay };
|