duckylib 0.1.2 → 0.1.4
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/buttons/Button.svelte +109 -0
- package/dist/components/buttons/Button.svelte.d.ts +10 -0
- package/dist/components/containers/AgeConfirm.svelte +100 -0
- package/dist/components/containers/AgeConfirm.svelte.d.ts +7 -0
- package/dist/components/containers/Column.svelte +3 -2
- package/dist/components/containers/Column.svelte.d.ts +1 -1
- package/dist/components/containers/HorizontalRule.svelte +39 -0
- package/dist/components/containers/HorizontalRule.svelte.d.ts +13 -0
- package/dist/components/containers/Loading.svelte +62 -0
- package/dist/components/containers/Loading.svelte.d.ts +3 -0
- package/dist/components/containers/Row.svelte +1 -0
- package/dist/components/containers/cards/{LinkCard.svelte → ButtonCard.svelte} +18 -10
- package/dist/components/containers/cards/ButtonCard.svelte.d.ts +15 -0
- package/dist/components/containers/navigation/Header.svelte +40 -4
- package/dist/components/containers/navigation/Header.svelte.d.ts +11 -2
- package/dist/components/containers/navigation/SearchBar.svelte +539 -0
- package/dist/components/containers/navigation/SearchBar.svelte.d.ts +22 -0
- package/dist/components/text/Code.svelte +19 -0
- package/dist/components/text/Code.svelte.d.ts +11 -0
- package/dist/components/text/Markdown.svelte +62 -8
- package/dist/components/text/Markdown.svelte.d.ts +8 -3
- package/dist/components/text/Text.svelte +40 -0
- package/dist/components/text/Text.svelte.d.ts +15 -0
- package/dist/components/text/Typewriter.svelte +3 -3
- package/dist/index.d.ts +0 -24
- package/dist/index.js +49 -2
- package/dist/styles/fonts.css +1 -0
- package/dist/styles/theme.css +4 -0
- package/package.json +4 -3
- package/dist/components/containers/cards/LinkCard.svelte.d.ts +0 -14
- package/dist/components/index.d.ts +0 -1
- package/dist/components/index.js +0 -2
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { browser } from "$app/environment";
|
|
3
|
+
import { goto } from "$app/navigation";
|
|
4
|
+
import { onMount } from "svelte";
|
|
5
|
+
import Heading from "../text/Heading.svelte";
|
|
6
|
+
import Text from "../text/Text.svelte"
|
|
7
|
+
import { getTheme } from "../../index.js";
|
|
8
|
+
|
|
9
|
+
interface ButtonProps {
|
|
10
|
+
label: string;
|
|
11
|
+
type?: "success" | "danger" | "primary" | "secondary";
|
|
12
|
+
onclick?: (e: MouseEvent) => void;
|
|
13
|
+
href?: string | null;
|
|
14
|
+
|
|
15
|
+
size?: "large" | "normal";
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
let {
|
|
19
|
+
label,
|
|
20
|
+
type = "secondary",
|
|
21
|
+
onclick = (e) => {
|
|
22
|
+
console.log(`Voided Mouse Event`, e.timeStamp);
|
|
23
|
+
},
|
|
24
|
+
href = null,
|
|
25
|
+
size = "normal"
|
|
26
|
+
}: ButtonProps = $props();
|
|
27
|
+
|
|
28
|
+
let systemMode = browser ? window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light" : "dark";
|
|
29
|
+
let theme = $state(getTheme());
|
|
30
|
+
|
|
31
|
+
onMount(() => {
|
|
32
|
+
window.addEventListener("theme", (e: any) => {
|
|
33
|
+
theme = e.detail.theme;
|
|
34
|
+
})
|
|
35
|
+
})
|
|
36
|
+
</script>
|
|
37
|
+
|
|
38
|
+
<button
|
|
39
|
+
data-theme="{theme === "system" ? systemMode : theme}"
|
|
40
|
+
class={type}
|
|
41
|
+
onclick={(e) => {
|
|
42
|
+
if (browser && href) {
|
|
43
|
+
goto(href);
|
|
44
|
+
} else if (browser) {
|
|
45
|
+
onclick(e);
|
|
46
|
+
}
|
|
47
|
+
}}><Heading size={size === "normal" ? 6 : 5} inheritColor={true}>{label}</Heading></button
|
|
48
|
+
>
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
<style>
|
|
52
|
+
@import url("../../styles/globals.css");
|
|
53
|
+
|
|
54
|
+
button {
|
|
55
|
+
cursor: pointer;
|
|
56
|
+
|
|
57
|
+
display: flex;
|
|
58
|
+
flex-direction: row;
|
|
59
|
+
|
|
60
|
+
align-items: center;
|
|
61
|
+
justify-content: center;
|
|
62
|
+
|
|
63
|
+
outline: none;
|
|
64
|
+
border: none;
|
|
65
|
+
|
|
66
|
+
padding: 0.66em 1.33em;
|
|
67
|
+
border-radius: var(--border-md);
|
|
68
|
+
background-color: var(--overlay-2);
|
|
69
|
+
color: var(--text);
|
|
70
|
+
|
|
71
|
+
font-size: 0.7em;
|
|
72
|
+
font-weight: 700;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
button:hover {
|
|
76
|
+
transition: all 0.2s;
|
|
77
|
+
transform: scale(1.02) translateY(1px);
|
|
78
|
+
filter: brightness(1.1);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
button:not(:hover) {
|
|
82
|
+
transition: all 0.2s;
|
|
83
|
+
transform: scale(1) translateY(0);
|
|
84
|
+
filter: brightness(1);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.danger {
|
|
88
|
+
background-color: var(--maroon);
|
|
89
|
+
color: var(--base);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.primary {
|
|
93
|
+
background-color: var(--accent);
|
|
94
|
+
color: var(--base);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.secondary {
|
|
98
|
+
background-color: var(--overlay-0);
|
|
99
|
+
color: var(--base);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.success {
|
|
103
|
+
background-color: var(--green);
|
|
104
|
+
color: var(--base);
|
|
105
|
+
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/* .success[data-theme="dark"] */
|
|
109
|
+
</style>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
interface ButtonProps {
|
|
2
|
+
label: string;
|
|
3
|
+
type?: "success" | "danger" | "primary" | "secondary";
|
|
4
|
+
onclick?: (e: MouseEvent) => void;
|
|
5
|
+
href?: string | null;
|
|
6
|
+
size?: "large" | "normal";
|
|
7
|
+
}
|
|
8
|
+
declare const Button: import("svelte").Component<ButtonProps, {}, "">;
|
|
9
|
+
type Button = ReturnType<typeof Button>;
|
|
10
|
+
export default Button;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { getAgeVerified } from "../../index.js";
|
|
3
|
+
import Heading from "../text/Heading.svelte";
|
|
4
|
+
import Column from "./Column.svelte";
|
|
5
|
+
import Text from "../text/Text.svelte"
|
|
6
|
+
import Row from "./Row.svelte";
|
|
7
|
+
import { onMount } from "svelte";
|
|
8
|
+
import Button from "../buttons/Button.svelte";
|
|
9
|
+
import { browser } from "$app/environment";
|
|
10
|
+
import Symbol from "../text/Symbol.svelte";
|
|
11
|
+
import { goto } from "$app/navigation";
|
|
12
|
+
|
|
13
|
+
interface AgeConfirmProps {
|
|
14
|
+
age?: number;
|
|
15
|
+
references?: ("substances" | "sexual" | "death" | "sh")[];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
let {age = 18, references = []}: AgeConfirmProps = $props();
|
|
19
|
+
|
|
20
|
+
const referenceMap = {
|
|
21
|
+
"substances": "Alcohol, Drugs, and Substance Abuse",
|
|
22
|
+
"sexual": "Sexual Content",
|
|
23
|
+
"death": "Death or Dying",
|
|
24
|
+
"sh": "Suicide, self-harm, or suicidal ideation"
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
let ageVerified = $state(getAgeVerified());
|
|
28
|
+
|
|
29
|
+
let loaded = $state(false);
|
|
30
|
+
|
|
31
|
+
onMount(() => {
|
|
32
|
+
window.onscroll = (e) => {
|
|
33
|
+
if(!ageVerified) {
|
|
34
|
+
e.preventDefault();
|
|
35
|
+
window.scrollTo({top: 0, behavior: "instant"})
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
setTimeout(() => {
|
|
40
|
+
loaded = true;
|
|
41
|
+
}, 5e2)
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
function setAgeVerified(v: boolean) {
|
|
45
|
+
if(browser) {
|
|
46
|
+
window.localStorage.setItem("av", `${v}`)
|
|
47
|
+
|
|
48
|
+
window.location.reload();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
</script>
|
|
52
|
+
|
|
53
|
+
{#if !ageVerified}
|
|
54
|
+
<div>
|
|
55
|
+
<Column marginLeftPx="auto" marginRightPx="auto" textWrap={true}>
|
|
56
|
+
<Column heightPx="fit" textWrap={true} widthPercent={95}>
|
|
57
|
+
{#if loaded}
|
|
58
|
+
<Symbol name="no_adult_content" sizePx={64} />
|
|
59
|
+
<Heading size={2} weight="boldest">{loaded ? `Are you ${age}+?` : ""}</Heading>
|
|
60
|
+
{#if references.length > 0}
|
|
61
|
+
<Heading size={4} weight="boldest">This site contains references to</Heading>
|
|
62
|
+
<ul style="text-align: left;">
|
|
63
|
+
{#each references.map(r => referenceMap[r]) as ref}
|
|
64
|
+
<Text><li>{loaded ? ref : ""}</li></Text>
|
|
65
|
+
{/each}
|
|
66
|
+
</ul>
|
|
67
|
+
{/if}
|
|
68
|
+
<Text weight="normal" classList={["italic"]}>{loaded ? `You must be at least ${age} years old to access this website.` : ""}</Text>
|
|
69
|
+
<Row>
|
|
70
|
+
<Button label="I am {age} or older" size="large" type="success" onclick={(e) => {setAgeVerified(true)}} />
|
|
71
|
+
<Button label="I am younger than {age}" size="large" type="danger" onclick={(e) => {setAgeVerified(false)}} />
|
|
72
|
+
</Row>
|
|
73
|
+
{/if}
|
|
74
|
+
</Column>
|
|
75
|
+
</Column>
|
|
76
|
+
</div>
|
|
77
|
+
{/if}
|
|
78
|
+
|
|
79
|
+
<style>
|
|
80
|
+
@import url("../../styles/globals.css");
|
|
81
|
+
|
|
82
|
+
div {
|
|
83
|
+
position: absolute;
|
|
84
|
+
top: 0;
|
|
85
|
+
left: 0;
|
|
86
|
+
|
|
87
|
+
width: 100%;
|
|
88
|
+
height: 110%;
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
padding: 0.33em;
|
|
92
|
+
margin: 0;
|
|
93
|
+
box-sizing: border-box;
|
|
94
|
+
|
|
95
|
+
background-color: var(--base);
|
|
96
|
+
color: var(--text);
|
|
97
|
+
|
|
98
|
+
z-index: 1000;
|
|
99
|
+
}
|
|
100
|
+
</style>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
interface AgeConfirmProps {
|
|
2
|
+
age?: number;
|
|
3
|
+
references?: ("substances" | "sexual" | "death" | "sh")[];
|
|
4
|
+
}
|
|
5
|
+
declare const AgeConfirm: import("svelte").Component<AgeConfirmProps, {}, "">;
|
|
6
|
+
type AgeConfirm = ReturnType<typeof AgeConfirm>;
|
|
7
|
+
export default AgeConfirm;
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
justifyContent?: "center" | "flex-start" | "flex-end" | "space-between";
|
|
8
8
|
alignItems?: "center" | "flex-start" | "flex-end" | "space-between";
|
|
9
9
|
textAlign?: "center" | "left" | "right" | "justify";
|
|
10
|
-
gapEm?: number;
|
|
10
|
+
gapEm?: number | "auto";
|
|
11
11
|
textWrap?: boolean;
|
|
12
12
|
flexWrap?: boolean;
|
|
13
13
|
|
|
@@ -37,13 +37,14 @@ flex-direction: column;
|
|
|
37
37
|
flex-wrap: ${flexWrap ? "wrap" : "nowrap"};
|
|
38
38
|
justify-content: ${justifyContent};
|
|
39
39
|
align-items: ${alignItems};
|
|
40
|
-
gap: ${gapEm}em;
|
|
40
|
+
gap: ${gapEm === "auto" ? "auto" : `${gapEm}em`};
|
|
41
41
|
border-radius: ${borderRadiusPx}px;
|
|
42
42
|
text-align: ${textAlign};
|
|
43
43
|
margin-left: ${marginLeftPx === "auto" ? "auto" : `${marginLeftPx}px`};
|
|
44
44
|
margin-right: ${marginRightPx === "auto" ? "auto" : `${marginRightPx}px`};
|
|
45
45
|
margin-bottom: ${marginBottomPx === "auto" ? "auto" : `${marginBottomPx}px`};
|
|
46
46
|
margin-top: ${marginTopPx === "auto" ? "auto" : `${marginTopPx}px`};
|
|
47
|
+
background-color: ${backgroundColor};
|
|
47
48
|
|
|
48
49
|
`}">
|
|
49
50
|
{@render children()}
|
|
@@ -4,7 +4,7 @@ interface ColumnProps {
|
|
|
4
4
|
justifyContent?: "center" | "flex-start" | "flex-end" | "space-between";
|
|
5
5
|
alignItems?: "center" | "flex-start" | "flex-end" | "space-between";
|
|
6
6
|
textAlign?: "center" | "left" | "right" | "justify";
|
|
7
|
-
gapEm?: number;
|
|
7
|
+
gapEm?: number | "auto";
|
|
8
8
|
textWrap?: boolean;
|
|
9
9
|
flexWrap?: boolean;
|
|
10
10
|
backgroundColor?: string | "transparent";
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
|
|
3
|
+
interface RowProps {
|
|
4
|
+
textAlign?: "center" | "left" | "right";
|
|
5
|
+
|
|
6
|
+
borderRadiusPx?: number;
|
|
7
|
+
|
|
8
|
+
widthPx?: number | "fill" | "fit";
|
|
9
|
+
widthPercent?: number;
|
|
10
|
+
|
|
11
|
+
marginRightPx?: number | "auto";
|
|
12
|
+
marginLeftPx?: number | "auto";
|
|
13
|
+
marginTopPx?: number | "auto";
|
|
14
|
+
marginBottomPx?: number | "auto";
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const { textAlign = "center", borderRadiusPx = 0, widthPx = "fill", widthPercent = 0, marginBottomPx = 0, marginLeftPx = 0, marginRightPx = 0, marginTopPx = 0 }: RowProps = $props();
|
|
18
|
+
</script>
|
|
19
|
+
|
|
20
|
+
<hr style="{`
|
|
21
|
+
width: ${widthPercent !== 0 ? `${widthPercent}%` : widthPx === "fill" ? "100%" : widthPx === "fit" ? "auto" : `${widthPx}px`};
|
|
22
|
+
${borderRadiusPx > 0 ? `border-radius: ${borderRadiusPx}px;` : ""}
|
|
23
|
+
text-align: ${textAlign};
|
|
24
|
+
margin-left: ${marginLeftPx === "auto" ? "auto" : `${marginLeftPx}px`};
|
|
25
|
+
margin-right: ${marginRightPx === "auto" ? "auto" : `${marginRightPx}px`};
|
|
26
|
+
margin-bottom: ${marginBottomPx === "auto" ? "auto" : `${marginBottomPx}px`};
|
|
27
|
+
margin-top: ${marginTopPx === "auto" ? "auto" : `${marginTopPx}px`};
|
|
28
|
+
`}" />
|
|
29
|
+
|
|
30
|
+
<style>
|
|
31
|
+
@import url("../../styles/globals.css");
|
|
32
|
+
|
|
33
|
+
hr {
|
|
34
|
+
background-color: var(--crust) !important;
|
|
35
|
+
height: 3px;
|
|
36
|
+
border: none;
|
|
37
|
+
border-radius: var(--border-md);
|
|
38
|
+
}
|
|
39
|
+
</style>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
interface RowProps {
|
|
2
|
+
textAlign?: "center" | "left" | "right";
|
|
3
|
+
borderRadiusPx?: number;
|
|
4
|
+
widthPx?: number | "fill" | "fit";
|
|
5
|
+
widthPercent?: number;
|
|
6
|
+
marginRightPx?: number | "auto";
|
|
7
|
+
marginLeftPx?: number | "auto";
|
|
8
|
+
marginTopPx?: number | "auto";
|
|
9
|
+
marginBottomPx?: number | "auto";
|
|
10
|
+
}
|
|
11
|
+
declare const HorizontalRule: import("svelte").Component<RowProps, {}, "">;
|
|
12
|
+
type HorizontalRule = ReturnType<typeof HorizontalRule>;
|
|
13
|
+
export default HorizontalRule;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import Heading from "../text/Heading.svelte";
|
|
3
|
+
import Column from "./Column.svelte";
|
|
4
|
+
import Text from "../text/Text.svelte"
|
|
5
|
+
import { onMount } from "svelte";
|
|
6
|
+
import Symbol from "../text/Symbol.svelte";
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
let loaded = $state(false);
|
|
10
|
+
|
|
11
|
+
onMount(() => {
|
|
12
|
+
window.onscroll = (e) => {
|
|
13
|
+
if(!loaded) {
|
|
14
|
+
e.preventDefault();
|
|
15
|
+
window.scrollTo({top: 0, behavior: "instant"})
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
setTimeout(() => {
|
|
20
|
+
loaded = true;
|
|
21
|
+
|
|
22
|
+
}, 7e2)
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
</script>
|
|
26
|
+
|
|
27
|
+
{#if !loaded}
|
|
28
|
+
<div>
|
|
29
|
+
<Column marginLeftPx="auto" marginRightPx="auto" textWrap={true}>
|
|
30
|
+
<Column heightPx="fit" textWrap={true} widthPercent={95}>
|
|
31
|
+
<Symbol name="hourglass" sizePx={64} />
|
|
32
|
+
<Heading size={2} weight="boldest">Loading...</Heading>
|
|
33
|
+
|
|
34
|
+
<Text weight="normal" classList={["italic"]}>Your content is loading. Thank you for your patience.</Text>
|
|
35
|
+
|
|
36
|
+
</Column>
|
|
37
|
+
</Column>
|
|
38
|
+
</div>
|
|
39
|
+
{/if}
|
|
40
|
+
|
|
41
|
+
<style>
|
|
42
|
+
@import url("../../styles/globals.css");
|
|
43
|
+
|
|
44
|
+
div {
|
|
45
|
+
position: absolute;
|
|
46
|
+
top: 0;
|
|
47
|
+
left: 0;
|
|
48
|
+
|
|
49
|
+
width: 100%;
|
|
50
|
+
height: 110%;
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
padding: 0.33em;
|
|
54
|
+
margin: 0;
|
|
55
|
+
box-sizing: border-box;
|
|
56
|
+
|
|
57
|
+
background-color: var(--base);
|
|
58
|
+
color: var(--text);
|
|
59
|
+
|
|
60
|
+
z-index: 2000;
|
|
61
|
+
}
|
|
62
|
+
</style>
|
|
@@ -44,6 +44,7 @@ margin-left: ${marginLeftPx === "auto" ? "auto" : `${marginLeftPx}px`};
|
|
|
44
44
|
margin-right: ${marginRightPx === "auto" ? "auto" : `${marginRightPx}px`};
|
|
45
45
|
margin-bottom: ${marginBottomPx === "auto" ? "auto" : `${marginBottomPx}px`};
|
|
46
46
|
margin-top: ${marginTopPx === "auto" ? "auto" : `${marginTopPx}px`};
|
|
47
|
+
background-color: ${backgroundColor};
|
|
47
48
|
`}">
|
|
48
49
|
{@render children()}
|
|
49
50
|
</div>
|
|
@@ -1,27 +1,27 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import type { Snippet } from "svelte";
|
|
3
2
|
import Column from "../Column.svelte";
|
|
4
3
|
import Symbol from "../../text/Symbol.svelte";
|
|
5
4
|
import Row from "../Row.svelte";
|
|
6
5
|
import { browser } from "$app/environment";
|
|
7
|
-
import
|
|
6
|
+
import Text from "../../text/Text.svelte";
|
|
8
7
|
|
|
9
|
-
interface
|
|
8
|
+
interface ButtonCardButton {
|
|
10
9
|
text: string;
|
|
11
|
-
href: string;
|
|
12
10
|
type: "success" | "danger" | "primary" | "secondary";
|
|
11
|
+
href?: string;
|
|
12
|
+
onclick?: ((e: MouseEvent) => void) | null;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
interface
|
|
15
|
+
interface ButtonCardProps {
|
|
16
16
|
text: string;
|
|
17
17
|
title?: string;
|
|
18
18
|
|
|
19
|
-
buttons:
|
|
19
|
+
buttons: ButtonCardButton[];
|
|
20
20
|
|
|
21
21
|
symbol?: string | null;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
let { text, symbol = null, title = "", buttons }:
|
|
24
|
+
let { text, symbol = null, title = "", buttons }: ButtonCardProps = $props();
|
|
25
25
|
</script>
|
|
26
26
|
|
|
27
27
|
<div>
|
|
@@ -37,9 +37,13 @@
|
|
|
37
37
|
<p class="font-semibold">{text}</p>
|
|
38
38
|
<Row gapEm={0.33} justifyContent="flex-start">
|
|
39
39
|
{#each buttons as button}
|
|
40
|
-
<button class={button.type} onclick={() => {
|
|
41
|
-
if(
|
|
42
|
-
|
|
40
|
+
<button class={button.type} data-icon="{button.href && !button.onclick}" onclick={async (e) => {
|
|
41
|
+
if(button.onclick) {
|
|
42
|
+
await button.onclick(e)
|
|
43
|
+
} else {
|
|
44
|
+
if(browser && button.href) window.open(button.href, "_blank")
|
|
45
|
+
}
|
|
46
|
+
}}>{#if button.href && !button.onclick}<Row heightPx="fit" gapEm={0.66}><Text inheritColor={true} weight="bolder">{button.text}</Text><Symbol name="open_in_new" sizePx={18} inheritColor={true} /></Row>{:else}<Text inheritColor={true} weight="bolder">{button.text}</Text>{/if}</button>
|
|
43
47
|
{/each}
|
|
44
48
|
</Row>
|
|
45
49
|
</Column>
|
|
@@ -112,6 +116,10 @@
|
|
|
112
116
|
font-weight: 700;
|
|
113
117
|
}
|
|
114
118
|
|
|
119
|
+
button[data-icon="true"] {
|
|
120
|
+
padding: 0.33em 0.66em;
|
|
121
|
+
}
|
|
122
|
+
|
|
115
123
|
button:hover {
|
|
116
124
|
transition: all 0.2s;
|
|
117
125
|
transform: scale(1.02) translateY(1px);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
interface ButtonCardButton {
|
|
2
|
+
text: string;
|
|
3
|
+
type: "success" | "danger" | "primary" | "secondary";
|
|
4
|
+
href?: string;
|
|
5
|
+
onclick?: ((e: MouseEvent) => void) | null;
|
|
6
|
+
}
|
|
7
|
+
interface ButtonCardProps {
|
|
8
|
+
text: string;
|
|
9
|
+
title?: string;
|
|
10
|
+
buttons: ButtonCardButton[];
|
|
11
|
+
symbol?: string | null;
|
|
12
|
+
}
|
|
13
|
+
declare const ButtonCard: import("svelte").Component<ButtonCardProps, {}, "">;
|
|
14
|
+
type ButtonCard = ReturnType<typeof ButtonCard>;
|
|
15
|
+
export default ButtonCard;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { Snippet } from "svelte";
|
|
3
3
|
import Row from "../Row.svelte";
|
|
4
|
-
import { getTheme } from "../../../index.js";
|
|
4
|
+
import { getTheme, getUserData } from "../../../index.js";
|
|
5
5
|
import ThemeButton from "../../buttons/ThemeButton.svelte";
|
|
6
6
|
import { page } from "$app/state";
|
|
7
7
|
import { MediaQuery } from "svelte/reactivity";
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
import LoginButton from "../../buttons/LoginButton.svelte";
|
|
11
11
|
import UserToast from "../../buttons/UserToast.svelte";
|
|
12
12
|
import { PUBLIC_MOBILE_SIZE_PX, PUBLIC_TABLET_SIZE_PX } from "$env/static/public";
|
|
13
|
+
import SearchBar, { type QueryMapKeys, type SearchResult } from "./SearchBar.svelte";
|
|
13
14
|
|
|
14
15
|
interface HeaderNavigation {
|
|
15
16
|
label: string;
|
|
@@ -26,7 +27,16 @@
|
|
|
26
27
|
withThemeButton?: boolean;
|
|
27
28
|
withIcons?: boolean;
|
|
28
29
|
|
|
29
|
-
|
|
30
|
+
withSearchBar?: boolean;
|
|
31
|
+
onsearch?: ((query: string) => Promise<void>) | null;
|
|
32
|
+
onresult?: ((result: SearchResult) => Promise<void>) | null;
|
|
33
|
+
searchOptions?: SearchResult[];
|
|
34
|
+
searchQueryUrl?: string;
|
|
35
|
+
searchQueryObjMap?: {[key in QueryMapKeys]: string | null};
|
|
36
|
+
searchQueryDataKey?: string;
|
|
37
|
+
|
|
38
|
+
defaultNav?: boolean;
|
|
39
|
+
|
|
30
40
|
authFeatures?: boolean;
|
|
31
41
|
|
|
32
42
|
nav?: HeaderNavigation[];
|
|
@@ -41,12 +51,22 @@
|
|
|
41
51
|
|
|
42
52
|
let {
|
|
43
53
|
children = null,
|
|
54
|
+
|
|
44
55
|
withThemeButton = true,
|
|
45
56
|
withIcons = true,
|
|
57
|
+
defaultNav = true,
|
|
58
|
+
|
|
59
|
+
withSearchBar = false,
|
|
60
|
+
onsearch = null,
|
|
61
|
+
onresult = null,
|
|
62
|
+
searchOptions = [],
|
|
63
|
+
searchQueryDataKey,
|
|
64
|
+
searchQueryObjMap,
|
|
65
|
+
searchQueryUrl,
|
|
66
|
+
|
|
46
67
|
emoji = "🦆",
|
|
47
68
|
label = "",
|
|
48
69
|
|
|
49
|
-
user = null,
|
|
50
70
|
authFeatures = false,
|
|
51
71
|
|
|
52
72
|
nav = [],
|
|
@@ -56,7 +76,11 @@
|
|
|
56
76
|
return nav;
|
|
57
77
|
}
|
|
58
78
|
|
|
59
|
-
|
|
79
|
+
function getDefaultNav(): boolean {
|
|
80
|
+
return defaultNav;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if(getDefaultNav()) nav = [
|
|
60
84
|
{
|
|
61
85
|
label: "Home",
|
|
62
86
|
pathname: "/",
|
|
@@ -65,6 +89,14 @@
|
|
|
65
89
|
...getNav(),
|
|
66
90
|
];
|
|
67
91
|
|
|
92
|
+
function authFeaturesEnabled(): boolean {
|
|
93
|
+
return authFeatures;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
let user: Auth.User | null = $state(null);
|
|
97
|
+
|
|
98
|
+
if(authFeaturesEnabled()) user = getUserData();
|
|
99
|
+
|
|
68
100
|
const mobileQuery = new MediaQuery(`max-width: ${PUBLIC_MOBILE_SIZE_PX}px`);
|
|
69
101
|
const tabletQuery = new MediaQuery(`max-width: ${PUBLIC_TABLET_SIZE_PX}px`);
|
|
70
102
|
</script>
|
|
@@ -83,6 +115,7 @@
|
|
|
83
115
|
<Row widthPx="fit" gapEm={mobileQuery.current ? 1 : 1.33}>
|
|
84
116
|
{#each nav as n}
|
|
85
117
|
<a
|
|
118
|
+
target="{n.pathname.startsWith("/") ? "_self" : "_blank"}"
|
|
86
119
|
href={n.pathname}
|
|
87
120
|
class="font-semibold{page.url.pathname ===
|
|
88
121
|
n.pathname
|
|
@@ -121,6 +154,9 @@
|
|
|
121
154
|
</Row>
|
|
122
155
|
{/if}
|
|
123
156
|
<Row widthPx="fit">
|
|
157
|
+
{#if withSearchBar && onresult !== null}
|
|
158
|
+
<SearchBar {onsearch} {onresult} options={searchOptions} queryDataKey={searchQueryDataKey} queryObjMap={searchQueryObjMap} queryUrl={searchQueryUrl}/>
|
|
159
|
+
{/if}
|
|
124
160
|
{#if authFeatures}
|
|
125
161
|
<Row widthPx="fit">
|
|
126
162
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Snippet } from "svelte";
|
|
2
|
-
import type
|
|
2
|
+
import { type QueryMapKeys, type SearchResult } from "./SearchBar.svelte";
|
|
3
3
|
interface HeaderNavigation {
|
|
4
4
|
label: string;
|
|
5
5
|
pathname: string;
|
|
@@ -11,7 +11,16 @@ interface HeaderProps {
|
|
|
11
11
|
label?: string;
|
|
12
12
|
withThemeButton?: boolean;
|
|
13
13
|
withIcons?: boolean;
|
|
14
|
-
|
|
14
|
+
withSearchBar?: boolean;
|
|
15
|
+
onsearch?: ((query: string) => Promise<void>) | null;
|
|
16
|
+
onresult?: ((result: SearchResult) => Promise<void>) | null;
|
|
17
|
+
searchOptions?: SearchResult[];
|
|
18
|
+
searchQueryUrl?: string;
|
|
19
|
+
searchQueryObjMap?: {
|
|
20
|
+
[key in QueryMapKeys]: string | null;
|
|
21
|
+
};
|
|
22
|
+
searchQueryDataKey?: string;
|
|
23
|
+
defaultNav?: boolean;
|
|
15
24
|
authFeatures?: boolean;
|
|
16
25
|
nav?: HeaderNavigation[];
|
|
17
26
|
}
|