@damillora/iuno 0.2.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/README.md +44 -0
- package/dist/app.css +12 -0
- package/dist/components/form/Button.svelte +21 -0
- package/dist/components/form/Button.svelte.d.ts +8 -0
- package/dist/components/form/Input.svelte +10 -0
- package/dist/components/form/Input.svelte.d.ts +13 -0
- package/dist/components/form/InputButton.svelte +18 -0
- package/dist/components/form/InputButton.svelte.d.ts +7 -0
- package/dist/components/form/Label.svelte +12 -0
- package/dist/components/form/Label.svelte.d.ts +7 -0
- package/dist/components/form/LinkButton.svelte +20 -0
- package/dist/components/form/LinkButton.svelte.d.ts +8 -0
- package/dist/components/form/TagsInput.svelte +141 -0
- package/dist/components/form/TagsInput.svelte.d.ts +9 -0
- package/dist/components/form/TextInput.svelte +15 -0
- package/dist/components/form/TextInput.svelte.d.ts +7 -0
- package/dist/components/image/Image.svelte +31 -0
- package/dist/components/image/Image.svelte.d.ts +15 -0
- package/dist/components/image/LocalImage.svelte +7 -0
- package/dist/components/image/LocalImage.svelte.d.ts +15 -0
- package/dist/components/layout/Container.svelte +9 -0
- package/dist/components/layout/Container.svelte.d.ts +6 -0
- package/dist/components/layout/ContainerWithSidebar.svelte +25 -0
- package/dist/components/layout/ContainerWithSidebar.svelte.d.ts +8 -0
- package/dist/components/layout/Footer.svelte +9 -0
- package/dist/components/layout/Footer.svelte.d.ts +6 -0
- package/dist/components/layout/ScreenCenteredContainer.svelte +11 -0
- package/dist/components/layout/ScreenCenteredContainer.svelte.d.ts +6 -0
- package/dist/components/layout/Tabs.svelte +29 -0
- package/dist/components/layout/Tabs.svelte.d.ts +8 -0
- package/dist/components/nav/Nav.svelte +62 -0
- package/dist/components/nav/Nav.svelte.d.ts +7 -0
- package/dist/components/nav/NavButton.svelte +27 -0
- package/dist/components/nav/NavButton.svelte.d.ts +10 -0
- package/dist/components/nav/NavDropdown.svelte +46 -0
- package/dist/components/nav/NavDropdown.svelte.d.ts +7 -0
- package/dist/components/nav/NavHeader.svelte +9 -0
- package/dist/components/nav/NavHeader.svelte.d.ts +5 -0
- package/dist/components/nav/NavLink.svelte +24 -0
- package/dist/components/nav/NavLink.svelte.d.ts +9 -0
- package/dist/components/nav/Pagination.svelte +48 -0
- package/dist/components/nav/Pagination.svelte.d.ts +7 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +34 -0
- package/dist/utils/simple-pagination.d.ts +2 -0
- package/dist/utils/simple-pagination.js +53 -0
- package/package.json +72 -0
package/README.md
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# iuno
|
|
2
|
+
|
|
3
|
+
Damillora's next-generation component library and design system.
|
|
4
|
+
|
|
5
|
+
## Developing
|
|
6
|
+
|
|
7
|
+
Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
npm run dev
|
|
11
|
+
|
|
12
|
+
# or start the server and open the app in a new browser tab
|
|
13
|
+
npm run dev -- --open
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Everything inside `src/lib` is part of your library, everything inside `src/routes` can be used as a showcase or preview app.
|
|
17
|
+
|
|
18
|
+
## Building
|
|
19
|
+
|
|
20
|
+
To build your library:
|
|
21
|
+
|
|
22
|
+
```sh
|
|
23
|
+
npm pack
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
To create a production version of your showcase app:
|
|
27
|
+
|
|
28
|
+
```sh
|
|
29
|
+
npm run build
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
You can preview the production build with `npm run preview`.
|
|
33
|
+
|
|
34
|
+
> To deploy your app, you may need to install an [adapter](https://svelte.dev/docs/kit/adapters) for your target environment.
|
|
35
|
+
|
|
36
|
+
## Publishing
|
|
37
|
+
|
|
38
|
+
Go into the `package.json` and give your package the desired name through the `"name"` option. Also consider adding a `"license"` field and point it to a `LICENSE` file which you can create from a template (one popular option is the [MIT license](https://opensource.org/license/mit/)).
|
|
39
|
+
|
|
40
|
+
To publish your library to [npm](https://www.npmjs.com):
|
|
41
|
+
|
|
42
|
+
```sh
|
|
43
|
+
npm publish
|
|
44
|
+
```
|
package/dist/app.css
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
@import url('https://fonts.googleapis.com/css2?family=Sen:wght@400..800&display=swap');
|
|
2
|
+
@import "tailwindcss";
|
|
3
|
+
|
|
4
|
+
@theme {
|
|
5
|
+
--font-sans: "Sen", sans-serif;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
@theme inline {
|
|
9
|
+
--color-primary: var(--color-blue-800);
|
|
10
|
+
--color-primary-content: var(--color-blue-100);
|
|
11
|
+
--color-primary-hover: var(--color-blue-200);
|
|
12
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { MouseEventHandler } from "svelte/elements";
|
|
3
|
+
|
|
4
|
+
let {
|
|
5
|
+
children,
|
|
6
|
+
primary = false,
|
|
7
|
+
large = false,
|
|
8
|
+
class: className = "",
|
|
9
|
+
...restProps
|
|
10
|
+
} = $props();
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<button
|
|
14
|
+
class={[
|
|
15
|
+
"py-1 px-4 rounded-lg text-blue-800 border border-blue-800 transition-colors cursor-pointer hover:bg-blue-100",
|
|
16
|
+
primary && "bg-blue-800 text-white hover:bg-blue-700",
|
|
17
|
+
large && "text-xl py-2 px-8",
|
|
18
|
+
className,
|
|
19
|
+
]}
|
|
20
|
+
{...restProps}>{@render children?.()}</button
|
|
21
|
+
>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
let { children, class: className = "", ...restProps } = $props();
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<div
|
|
6
|
+
class="group rounded-lg bg-gray-100 focus-within:bg-blue-100 focus-within:ring focus-within:ring-blue-800 transition-all flex flex-row cursor-text {className}"
|
|
7
|
+
{...restProps}
|
|
8
|
+
>
|
|
9
|
+
{@render children?.()}
|
|
10
|
+
</div>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export default Input;
|
|
2
|
+
type Input = {
|
|
3
|
+
$on?(type: string, callback: (e: any) => void): () => void;
|
|
4
|
+
$set?(props: Partial<$$ComponentProps>): void;
|
|
5
|
+
};
|
|
6
|
+
declare const Input: import("svelte").Component<{
|
|
7
|
+
children: any;
|
|
8
|
+
class?: string;
|
|
9
|
+
} & Record<string, any>, {}, "">;
|
|
10
|
+
type $$ComponentProps = {
|
|
11
|
+
children: any;
|
|
12
|
+
class?: string;
|
|
13
|
+
} & Record<string, any>;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
let {
|
|
3
|
+
primary = false,
|
|
4
|
+
children,
|
|
5
|
+
class: className = "",
|
|
6
|
+
...restProps
|
|
7
|
+
} = $props();
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
<button
|
|
11
|
+
class="cursor-pointer hover:bg-blue-100 rounded-r-lg transition-colors px-2 {className}"
|
|
12
|
+
class:bg-blue-800={primary}
|
|
13
|
+
class:hover:bg-blue-700={primary}
|
|
14
|
+
class:text-white={primary}
|
|
15
|
+
{...restProps}
|
|
16
|
+
>
|
|
17
|
+
{@render children?.()}
|
|
18
|
+
</button>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
let {
|
|
3
|
+
children,
|
|
4
|
+
for: forName = "",
|
|
5
|
+
class: className = "",
|
|
6
|
+
...restProps
|
|
7
|
+
} = $props();
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
<div class="font-medium mb-2 text-base text-left {className}" {...restProps}>
|
|
11
|
+
<label for={forName}>{@render children?.()}</label>
|
|
12
|
+
</div>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { MouseEventHandler } from "svelte/elements";
|
|
3
|
+
|
|
4
|
+
let {
|
|
5
|
+
href,
|
|
6
|
+
children,
|
|
7
|
+
primary = false,
|
|
8
|
+
class: className = "",
|
|
9
|
+
...restProps
|
|
10
|
+
} = $props();
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<a
|
|
14
|
+
class="inline-block py-1 px-4 rounded-lg border border-blue-800 font-bold transition-colors cursor-pointer hover:bg-blue-100 {className}"
|
|
15
|
+
class:bg-blue-800={primary}
|
|
16
|
+
class:text-white={primary}
|
|
17
|
+
class:hover:bg-blue-700={primary}
|
|
18
|
+
{href}
|
|
19
|
+
{...restProps}>{@render children?.()}</a
|
|
20
|
+
>
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { KeyboardEventHandler } from "svelte/elements";
|
|
3
|
+
import NavButton from "../nav/NavButton.svelte";
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
value = [],
|
|
7
|
+
class: className = "",
|
|
8
|
+
ontagchange,
|
|
9
|
+
name = "",
|
|
10
|
+
onautocomplete = undefined,
|
|
11
|
+
...restProps
|
|
12
|
+
} = $props();
|
|
13
|
+
let textValue = $state("");
|
|
14
|
+
let id = $state(0);
|
|
15
|
+
|
|
16
|
+
let timer: Timeout | undefined = $state();
|
|
17
|
+
|
|
18
|
+
let autocompleteEntries: string[] = $state([]);
|
|
19
|
+
let selectedAutocomplete: number | undefined = $state();
|
|
20
|
+
|
|
21
|
+
const appendTextValue = (textValue: string) => {
|
|
22
|
+
let newValue = value.concat(textValue);
|
|
23
|
+
if (ontagchange) {
|
|
24
|
+
ontagchange(newValue);
|
|
25
|
+
}
|
|
26
|
+
id += 1;
|
|
27
|
+
};
|
|
28
|
+
const onKeyDown: KeyboardEventHandler<HTMLInputElement> = (e) => {
|
|
29
|
+
if (e.key === "ArrowUp") {
|
|
30
|
+
e.preventDefault();
|
|
31
|
+
let len = autocompleteEntries.length;
|
|
32
|
+
if (selectedAutocomplete === undefined) {
|
|
33
|
+
selectedAutocomplete = (len + len - 1) % len;
|
|
34
|
+
} else {
|
|
35
|
+
selectedAutocomplete = (selectedAutocomplete + len - 1) % len;
|
|
36
|
+
}
|
|
37
|
+
return;
|
|
38
|
+
} else if (e.key === "ArrowDown") {
|
|
39
|
+
e.preventDefault();
|
|
40
|
+
let len = autocompleteEntries.length;
|
|
41
|
+
if (selectedAutocomplete === undefined) {
|
|
42
|
+
console.log("up");
|
|
43
|
+
selectedAutocomplete = 0;
|
|
44
|
+
} else {
|
|
45
|
+
console.log("up");
|
|
46
|
+
selectedAutocomplete = (selectedAutocomplete + 1) % len;
|
|
47
|
+
}
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
if (e.key === " ") {
|
|
51
|
+
e.preventDefault();
|
|
52
|
+
if (selectedAutocomplete !== undefined) {
|
|
53
|
+
console.log(autocompleteEntries[selectedAutocomplete]);
|
|
54
|
+
autocompleteSelect(autocompleteEntries[selectedAutocomplete]);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
if (textValue !== "") {
|
|
58
|
+
const exist = value.find((x) => x === textValue);
|
|
59
|
+
if (!exist) {
|
|
60
|
+
appendTextValue(textValue);
|
|
61
|
+
textValue = "";
|
|
62
|
+
id += 1;
|
|
63
|
+
}
|
|
64
|
+
} else {
|
|
65
|
+
textValue = "";
|
|
66
|
+
}
|
|
67
|
+
} else if (
|
|
68
|
+
e.key === "Backspace" &&
|
|
69
|
+
textValue === "" &&
|
|
70
|
+
value.length > 0
|
|
71
|
+
) {
|
|
72
|
+
removeTag(value[value.length - 1]);
|
|
73
|
+
} else if (onautocomplete) {
|
|
74
|
+
clearTimeout(timer);
|
|
75
|
+
timer = setTimeout(() => {
|
|
76
|
+
invokeAutocomplete(textValue);
|
|
77
|
+
}, 400);
|
|
78
|
+
}
|
|
79
|
+
autocompleteEntries = [];
|
|
80
|
+
};
|
|
81
|
+
const invokeAutocomplete = async (str: string) => {
|
|
82
|
+
let autocomplete: string[] = await onautocomplete(str);
|
|
83
|
+
autocomplete = autocomplete.filter((x) => !value.includes(x));
|
|
84
|
+
autocompleteEntries = autocomplete.slice(0, 5);
|
|
85
|
+
};
|
|
86
|
+
const removeTag = (i: string) => {
|
|
87
|
+
let newValue = value.filter((x) => x != i);
|
|
88
|
+
console.log(newValue);
|
|
89
|
+
if (ontagchange) {
|
|
90
|
+
ontagchange(newValue);
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
const autocompleteSelect = (str: string) => {
|
|
94
|
+
appendTextValue(str);
|
|
95
|
+
autocompleteEntries = [];
|
|
96
|
+
selectedAutocomplete = undefined;
|
|
97
|
+
textValue = "";
|
|
98
|
+
id += 1;
|
|
99
|
+
};
|
|
100
|
+
</script>
|
|
101
|
+
|
|
102
|
+
<div
|
|
103
|
+
class="relative group py-2 px-2 w-full rounded-lg transition-shadow {className} flex flex-row flex-wrap cursor-text"
|
|
104
|
+
{...restProps}
|
|
105
|
+
>
|
|
106
|
+
{#each value as item, i}
|
|
107
|
+
<div
|
|
108
|
+
class="text-sm mr-1 rounded-lg bg-blue-100 group-focus-within:bg-blue-200 transition-colors px-2 my-1 cursor-default flex flex-row items-center justify-center"
|
|
109
|
+
>
|
|
110
|
+
<span class="mr-1">{item}</span>
|
|
111
|
+
<button
|
|
112
|
+
class="cursor-pointer hover:text-red-800 rounded-full transition-colors"
|
|
113
|
+
type="button"
|
|
114
|
+
onclick={() => removeTag(item)}>✕</button
|
|
115
|
+
>
|
|
116
|
+
</div>
|
|
117
|
+
{/each}
|
|
118
|
+
<input
|
|
119
|
+
type="text"
|
|
120
|
+
autocomplete="off"
|
|
121
|
+
{name}
|
|
122
|
+
class="outline-none focus-within:outline-none grow min-w-32"
|
|
123
|
+
bind:value={textValue}
|
|
124
|
+
onkeydown={onKeyDown}
|
|
125
|
+
{...restProps}
|
|
126
|
+
/>
|
|
127
|
+
<div
|
|
128
|
+
class="hidden group-focus-within:flex absolute left-0 top-full bg-gray-100 flex-col rounded-lg *:w-full transition-all"
|
|
129
|
+
>
|
|
130
|
+
{#each autocompleteEntries as autocomplete, i}
|
|
131
|
+
<NavButton
|
|
132
|
+
active={selectedAutocomplete === i}
|
|
133
|
+
class="text-left"
|
|
134
|
+
type="button"
|
|
135
|
+
onclick={() => autocompleteSelect(autocomplete)}
|
|
136
|
+
>
|
|
137
|
+
{autocomplete}
|
|
138
|
+
</NavButton>
|
|
139
|
+
{/each}
|
|
140
|
+
</div>
|
|
141
|
+
</div>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
let {
|
|
3
|
+
type: typeName = "text",
|
|
4
|
+
value = $bindable(),
|
|
5
|
+
class: className = "",
|
|
6
|
+
...restProps
|
|
7
|
+
} = $props();
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
<input
|
|
11
|
+
type={typeName}
|
|
12
|
+
class="py-2 px-2 rounded-lg outline-none focus-within:outline-none transition-shadow w-full {className}"
|
|
13
|
+
bind:value
|
|
14
|
+
{...restProps}
|
|
15
|
+
/>
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { onMount } from "svelte";
|
|
3
|
+
import { get } from "svelte/store";
|
|
4
|
+
|
|
5
|
+
let { alt, src, class: className = "" } = $props();
|
|
6
|
+
let loading = $state(false);
|
|
7
|
+
let failed = $state(false);
|
|
8
|
+
|
|
9
|
+
onMount(() => {
|
|
10
|
+
const img = new Image();
|
|
11
|
+
img.src = src;
|
|
12
|
+
loading = true;
|
|
13
|
+
|
|
14
|
+
img.onload = () => {
|
|
15
|
+
loading = false;
|
|
16
|
+
failed = false;
|
|
17
|
+
};
|
|
18
|
+
img.onerror = () => {
|
|
19
|
+
loading = true;
|
|
20
|
+
failed = true;
|
|
21
|
+
};
|
|
22
|
+
});
|
|
23
|
+
</script>
|
|
24
|
+
|
|
25
|
+
{#if !failed}
|
|
26
|
+
<figure class={className} class:bg-gray-300={loading}>
|
|
27
|
+
<img class="w-full" {src} {alt} />
|
|
28
|
+
</figure>
|
|
29
|
+
{:else}
|
|
30
|
+
<div class="bg-red-300">There was an error loading this image.</div>
|
|
31
|
+
{/if}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export default Image;
|
|
2
|
+
type Image = {
|
|
3
|
+
$on?(type: string, callback: (e: any) => void): () => void;
|
|
4
|
+
$set?(props: Partial<$$ComponentProps>): void;
|
|
5
|
+
};
|
|
6
|
+
declare const Image: import("svelte").Component<{
|
|
7
|
+
alt: any;
|
|
8
|
+
src: any;
|
|
9
|
+
class?: string;
|
|
10
|
+
}, {}, "">;
|
|
11
|
+
type $$ComponentProps = {
|
|
12
|
+
alt: any;
|
|
13
|
+
src: any;
|
|
14
|
+
class?: string;
|
|
15
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export default LocalImage;
|
|
2
|
+
type LocalImage = {
|
|
3
|
+
$on?(type: string, callback: (e: any) => void): () => void;
|
|
4
|
+
$set?(props: Partial<$$ComponentProps>): void;
|
|
5
|
+
};
|
|
6
|
+
declare const LocalImage: import("svelte").Component<{
|
|
7
|
+
alt: any;
|
|
8
|
+
src: any;
|
|
9
|
+
class?: string;
|
|
10
|
+
}, {}, "">;
|
|
11
|
+
type $$ComponentProps = {
|
|
12
|
+
alt: any;
|
|
13
|
+
src: any;
|
|
14
|
+
class?: string;
|
|
15
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
let {
|
|
3
|
+
sidebar = undefined,
|
|
4
|
+
contents = undefined,
|
|
5
|
+
reverse = false,
|
|
6
|
+
class: className = "",
|
|
7
|
+
} = $props();
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
<div
|
|
11
|
+
class="flex lg:flex-row {className}"
|
|
12
|
+
class:flex-col-reverse={reverse}
|
|
13
|
+
class:flex-col={!reverse}
|
|
14
|
+
>
|
|
15
|
+
<div
|
|
16
|
+
class="relative w-full lg:w-1/3 2xl:w-1/4 flex flex-col min-h-[calc(100vh-4rem)] pb-8"
|
|
17
|
+
>
|
|
18
|
+
{@render sidebar?.()}
|
|
19
|
+
</div>
|
|
20
|
+
<div
|
|
21
|
+
class="relative w-full lg:w-2/3 2xl:w-3/4 flex flex-col min-h-[calc(100vh-4rem)] pb-8"
|
|
22
|
+
>
|
|
23
|
+
{@render contents?.()}
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
let { children, class: className = "" } = $props();
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<div
|
|
6
|
+
class="min-h-[calc(100vh-4rem)] w-full flex flex-col items-center justify-center p-8 {className}"
|
|
7
|
+
>
|
|
8
|
+
<div class="text-center lg:w-3/4 xl:w-1/2">
|
|
9
|
+
{@render children?.()}
|
|
10
|
+
</div>
|
|
11
|
+
</div>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from "svelte";
|
|
3
|
+
import NavButton from "../nav/NavButton.svelte";
|
|
4
|
+
|
|
5
|
+
let { margin = true, tabs, contents, class: className = "" } = $props();
|
|
6
|
+
|
|
7
|
+
let currentTab = $state(0);
|
|
8
|
+
const changeTab = (tab: number) => {
|
|
9
|
+
currentTab = tab;
|
|
10
|
+
};
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<div class="flex flex-row mx-4 my-2">
|
|
14
|
+
<div class="bg-gray-100 rounded-lg">
|
|
15
|
+
{#each tabs as tab, i}
|
|
16
|
+
<NavButton
|
|
17
|
+
active={currentTab === i}
|
|
18
|
+
onclick={() => changeTab(i)}
|
|
19
|
+
class={i > 0 ? "ml-1" : ""}
|
|
20
|
+
>
|
|
21
|
+
{tab}
|
|
22
|
+
</NavButton>
|
|
23
|
+
{/each}
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|
|
26
|
+
|
|
27
|
+
<div class:mx-4={margin} class:my-2={margin} class={className}>
|
|
28
|
+
{@render contents?.(currentTab)}
|
|
29
|
+
</div>
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
let menuVisible = $state(false);
|
|
3
|
+
let {
|
|
4
|
+
logoItem = undefined,
|
|
5
|
+
centerItems = undefined,
|
|
6
|
+
rightItems = undefined,
|
|
7
|
+
} = $props();
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
<nav class="w-full bg-blue-50">
|
|
11
|
+
<div class="relative container mx-auto h-16 py-2 px-4 flex flex-row items-center z-30">
|
|
12
|
+
<a
|
|
13
|
+
href="/"
|
|
14
|
+
class="text-white font-bold hover:bg-blue-100 py-2 px-4 rounded-lg transition-colors md:mr-1"
|
|
15
|
+
>
|
|
16
|
+
<div class="flex flex-row items-center text-blue-800">
|
|
17
|
+
{@render logoItem?.()}
|
|
18
|
+
</div>
|
|
19
|
+
</a>
|
|
20
|
+
<div
|
|
21
|
+
class="flex flex-row md:flex-row items-end md:items-start justify-end w-full"
|
|
22
|
+
>
|
|
23
|
+
<button
|
|
24
|
+
aria-label="Menu"
|
|
25
|
+
class="md:hidden py-2 px-2 hover:bg-blue-100 rounded-lg cursor-pointer transition-colors"
|
|
26
|
+
class:bg-blue-200={menuVisible}
|
|
27
|
+
class:text-blue-800={menuVisible}
|
|
28
|
+
class:hover:bg-blue-200={menuVisible}
|
|
29
|
+
class:hover:text-blue-800={menuVisible}
|
|
30
|
+
onclick={() => {
|
|
31
|
+
menuVisible = !menuVisible;
|
|
32
|
+
}}
|
|
33
|
+
>
|
|
34
|
+
<svg
|
|
35
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
36
|
+
fill="none"
|
|
37
|
+
viewBox="0 0 24 24"
|
|
38
|
+
stroke-width="1.5"
|
|
39
|
+
stroke="currentColor"
|
|
40
|
+
class="size-6"
|
|
41
|
+
>
|
|
42
|
+
<path
|
|
43
|
+
stroke-linecap="round"
|
|
44
|
+
stroke-linejoin="round"
|
|
45
|
+
d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5"
|
|
46
|
+
/>
|
|
47
|
+
</svg>
|
|
48
|
+
</button>
|
|
49
|
+
<div
|
|
50
|
+
class="absolute top-16 left-0 right-0 px-2 md:px-0 md:static flex flex-col md:flex-row md:flex md:grow bg-blue-50 md:bg-transparent pb-2 md:pb-0"
|
|
51
|
+
class:hidden={!menuVisible}
|
|
52
|
+
>
|
|
53
|
+
<div class="flex flex-col md:flex-row grow *:md:mr-1">
|
|
54
|
+
{@render centerItems?.()}
|
|
55
|
+
</div>
|
|
56
|
+
<div class="flex flex-col md:flex-row *:md:mr-1">
|
|
57
|
+
{@render rightItems?.()}
|
|
58
|
+
</div>
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
</nav>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
let {
|
|
3
|
+
onclick = undefined,
|
|
4
|
+
active = false,
|
|
5
|
+
class: className = "",
|
|
6
|
+
children,
|
|
7
|
+
disabled = false,
|
|
8
|
+
danger = false,
|
|
9
|
+
...restProps
|
|
10
|
+
} = $props();
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<button
|
|
14
|
+
class={[
|
|
15
|
+
"inline-block py-2 px-4 rounded-lg transition-colors cursor-pointer",
|
|
16
|
+
"hover:bg-blue-100",
|
|
17
|
+
active && "bg-blue-100 text-blue-800",
|
|
18
|
+
disabled && "bg-gray-200 text-gray-700 hover:bg-gray-100 cursor-default",
|
|
19
|
+
danger && "text-red-800 hover:bg-red-200",
|
|
20
|
+
className,
|
|
21
|
+
]}
|
|
22
|
+
{disabled}
|
|
23
|
+
{onclick}
|
|
24
|
+
{...restProps}
|
|
25
|
+
>
|
|
26
|
+
{@render children?.()}
|
|
27
|
+
</button>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
declare const NavButton: import("svelte").Component<{
|
|
2
|
+
onclick?: any;
|
|
3
|
+
active?: boolean;
|
|
4
|
+
class?: string;
|
|
5
|
+
children: any;
|
|
6
|
+
disabled?: boolean;
|
|
7
|
+
danger?: boolean;
|
|
8
|
+
} & Record<string, any>, {}, "">;
|
|
9
|
+
type NavButton = ReturnType<typeof NavButton>;
|
|
10
|
+
export default NavButton;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
let { class: className = "", children, dropdownList } = $props();
|
|
3
|
+
import { fade, fly } from "svelte/transition";
|
|
4
|
+
let isDropdownVisible = $state(false);
|
|
5
|
+
</script>
|
|
6
|
+
|
|
7
|
+
<div class="relative">
|
|
8
|
+
<button
|
|
9
|
+
class="group w-full py-2 px-4 rounded-lg hover:bg-blue-100 cursor-pointer text-left flex flex-row items-center transition-colors {className}"
|
|
10
|
+
class:bg-blue-200={isDropdownVisible}
|
|
11
|
+
class:text-blue-800={isDropdownVisible}
|
|
12
|
+
class:hover:bg-blue-200={isDropdownVisible}
|
|
13
|
+
class:hover:text-blue-800={isDropdownVisible}
|
|
14
|
+
onclick={() => {
|
|
15
|
+
isDropdownVisible = !isDropdownVisible;
|
|
16
|
+
}}
|
|
17
|
+
>
|
|
18
|
+
<span class="w-full ">
|
|
19
|
+
{@render children?.()}
|
|
20
|
+
</span>
|
|
21
|
+
<svg
|
|
22
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
23
|
+
fill="none"
|
|
24
|
+
viewBox="0 0 24 24"
|
|
25
|
+
stroke-width="1.5"
|
|
26
|
+
stroke="currentColor"
|
|
27
|
+
class="size-6 ml-2"
|
|
28
|
+
>
|
|
29
|
+
<path
|
|
30
|
+
stroke-linecap="round"
|
|
31
|
+
stroke-linejoin="round"
|
|
32
|
+
d="m19.5 8.25-7.5 7.5-7.5-7.5"
|
|
33
|
+
/>
|
|
34
|
+
</svg>
|
|
35
|
+
</button>
|
|
36
|
+
{#if isDropdownVisible}
|
|
37
|
+
<div
|
|
38
|
+
class="absolute top-10 w-full md:min-w-64 right-0 md:px-0 flex flex-col bg-blue-50 rounded-lg *:w-full transition-all"
|
|
39
|
+
transition:fade={{
|
|
40
|
+
duration: 50,
|
|
41
|
+
}}
|
|
42
|
+
>
|
|
43
|
+
{@render dropdownList?.()}
|
|
44
|
+
</div>
|
|
45
|
+
{/if}
|
|
46
|
+
</div>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
let {
|
|
3
|
+
href,
|
|
4
|
+
class: className = "",
|
|
5
|
+
active = false,
|
|
6
|
+
primary = false,
|
|
7
|
+
children,
|
|
8
|
+
...restProps
|
|
9
|
+
} = $props();
|
|
10
|
+
</script>
|
|
11
|
+
|
|
12
|
+
<a
|
|
13
|
+
{href}
|
|
14
|
+
class={[
|
|
15
|
+
"inline-block py-2 px-4 rounded-lg transition-colors",
|
|
16
|
+
" hover:bg-blue-100",
|
|
17
|
+
primary && "bg-blue-800 text-white hover:bg-blue-700 hover:text-white",
|
|
18
|
+
active && "bg-blue-200 text-blue-800",
|
|
19
|
+
className,
|
|
20
|
+
]}
|
|
21
|
+
{...restProps}
|
|
22
|
+
>
|
|
23
|
+
{@render children?.()}
|
|
24
|
+
</a>
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { paginate } from "../../utils/simple-pagination.js";
|
|
3
|
+
import NavButton from "./NavButton.svelte";
|
|
4
|
+
|
|
5
|
+
let { page, changePage, totalPages } = $props();
|
|
6
|
+
|
|
7
|
+
let pagination = $derived(paginate(page, totalPages));
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
<nav class="flex flex-row justify-center" aria-label="pagination">
|
|
11
|
+
<div class="flex flex-row justify-center bg-gray-100 rounded-lg">
|
|
12
|
+
{#if page > 1}
|
|
13
|
+
<NavButton
|
|
14
|
+
onclick={() => changePage(page - 1)}
|
|
15
|
+
disabled={page <= 1}
|
|
16
|
+
class="mr-1">Previous</NavButton
|
|
17
|
+
>
|
|
18
|
+
{/if}
|
|
19
|
+
<ul class="flex flex-row">
|
|
20
|
+
{#each pagination as pageEntry, i}
|
|
21
|
+
{#if pageEntry == "..."}
|
|
22
|
+
<li>
|
|
23
|
+
<span
|
|
24
|
+
class="inline-block py-2 px-4 rounded-lg font-medium"
|
|
25
|
+
>…</span
|
|
26
|
+
>
|
|
27
|
+
</li>
|
|
28
|
+
{:else}
|
|
29
|
+
<li>
|
|
30
|
+
<NavButton
|
|
31
|
+
class={i < pagination.length - 2 ? "mr-1" : ""}
|
|
32
|
+
onclick={() => changePage(pageEntry)}
|
|
33
|
+
aria-label="Go to page {pageEntry}"
|
|
34
|
+
active={page === pageEntry}>{pageEntry}</NavButton
|
|
35
|
+
>
|
|
36
|
+
</li>
|
|
37
|
+
{/if}
|
|
38
|
+
{/each}
|
|
39
|
+
</ul>
|
|
40
|
+
{#if page < totalPages}
|
|
41
|
+
<NavButton
|
|
42
|
+
class="ml-1"
|
|
43
|
+
onclick={() => changePage(page + 1)}
|
|
44
|
+
disabled={page >= totalPages}>Next</NavButton
|
|
45
|
+
>
|
|
46
|
+
{/if}
|
|
47
|
+
</div>
|
|
48
|
+
</nav>
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import Button from "./components/form/Button.svelte";
|
|
2
|
+
import Input from "./components/form/Input.svelte";
|
|
3
|
+
import InputButton from "./components/form/InputButton.svelte";
|
|
4
|
+
import Label from "./components/form/Label.svelte";
|
|
5
|
+
import LinkButton from "./components/form/LinkButton.svelte";
|
|
6
|
+
import TagsInput from "./components/form/TagsInput.svelte";
|
|
7
|
+
import TextInput from "./components/form/TextInput.svelte";
|
|
8
|
+
import Image from "./components/image/Image.svelte";
|
|
9
|
+
import LocalImage from "./components/image/LocalImage.svelte";
|
|
10
|
+
import Container from "./components/layout/Container.svelte";
|
|
11
|
+
import ContainerWithSidebar from "./components/layout/ContainerWithSidebar.svelte";
|
|
12
|
+
import Footer from "./components/layout/Footer.svelte";
|
|
13
|
+
import ScreenCenteredContainer from "./components/layout/ScreenCenteredContainer.svelte";
|
|
14
|
+
import Tabs from "./components/layout/Tabs.svelte";
|
|
15
|
+
import Nav from "./components/nav/Nav.svelte";
|
|
16
|
+
import NavButton from "./components/nav/NavButton.svelte";
|
|
17
|
+
import NavDropdown from "./components/nav/NavDropdown.svelte";
|
|
18
|
+
import NavHeader from "./components/nav/NavHeader.svelte";
|
|
19
|
+
import NavLink from "./components/nav/NavLink.svelte";
|
|
20
|
+
import Pagination from "./components/nav/Pagination.svelte";
|
|
21
|
+
export { Button, Input, InputButton, Label, LinkButton, TagsInput, TextInput, Image, LocalImage, Container, ContainerWithSidebar, Footer, ScreenCenteredContainer, Tabs, Nav, NavButton, NavDropdown, NavHeader, NavLink, Pagination, };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// Reexport your entry components here
|
|
2
|
+
// Form
|
|
3
|
+
import Button from "./components/form/Button.svelte";
|
|
4
|
+
import Input from "./components/form/Input.svelte";
|
|
5
|
+
import InputButton from "./components/form/InputButton.svelte";
|
|
6
|
+
import Label from "./components/form/Label.svelte";
|
|
7
|
+
import LinkButton from "./components/form/LinkButton.svelte";
|
|
8
|
+
import TagsInput from "./components/form/TagsInput.svelte";
|
|
9
|
+
import TextInput from "./components/form/TextInput.svelte";
|
|
10
|
+
// Image
|
|
11
|
+
import Image from "./components/image/Image.svelte";
|
|
12
|
+
import LocalImage from "./components/image/LocalImage.svelte";
|
|
13
|
+
// Layout
|
|
14
|
+
import Container from "./components/layout/Container.svelte";
|
|
15
|
+
import ContainerWithSidebar from "./components/layout/ContainerWithSidebar.svelte";
|
|
16
|
+
import Footer from "./components/layout/Footer.svelte";
|
|
17
|
+
import ScreenCenteredContainer from "./components/layout/ScreenCenteredContainer.svelte";
|
|
18
|
+
import Tabs from "./components/layout/Tabs.svelte";
|
|
19
|
+
// Nav
|
|
20
|
+
import Nav from "./components/nav/Nav.svelte";
|
|
21
|
+
import NavButton from "./components/nav/NavButton.svelte";
|
|
22
|
+
import NavDropdown from "./components/nav/NavDropdown.svelte";
|
|
23
|
+
import NavHeader from "./components/nav/NavHeader.svelte";
|
|
24
|
+
import NavLink from "./components/nav/NavLink.svelte";
|
|
25
|
+
import Pagination from "./components/nav/Pagination.svelte";
|
|
26
|
+
export {
|
|
27
|
+
// Form
|
|
28
|
+
Button, Input, InputButton, Label, LinkButton, TagsInput, TextInput,
|
|
29
|
+
// Image
|
|
30
|
+
Image, LocalImage,
|
|
31
|
+
// Layout
|
|
32
|
+
Container, ContainerWithSidebar, Footer, ScreenCenteredContainer, Tabs,
|
|
33
|
+
// Nav
|
|
34
|
+
Nav, NavButton, NavDropdown, NavHeader, NavLink, Pagination, };
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// Implementation in ES6
|
|
2
|
+
// https://gist.github.com/kottenator/9d936eb3e4e3c3e02598
|
|
3
|
+
const paginate = (c, m) => {
|
|
4
|
+
let current = c, last = m, delta = 1, left = current - delta, right = current + delta + 1, range = [], rangeWithDots = [], l;
|
|
5
|
+
for (let i = 1; i <= last; i++) {
|
|
6
|
+
if (i == 1 || i == last || (i >= left && i < right)) {
|
|
7
|
+
range.push(i);
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
for (let i of range) {
|
|
11
|
+
if (l) {
|
|
12
|
+
if (i - l === 2) {
|
|
13
|
+
rangeWithDots.push(l + 1);
|
|
14
|
+
}
|
|
15
|
+
else if (i - l !== 1) {
|
|
16
|
+
rangeWithDots.push("...");
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
rangeWithDots.push(i);
|
|
20
|
+
l = i;
|
|
21
|
+
}
|
|
22
|
+
return rangeWithDots;
|
|
23
|
+
};
|
|
24
|
+
export { paginate };
|
|
25
|
+
/*
|
|
26
|
+
Test it:
|
|
27
|
+
|
|
28
|
+
for (let i = 1, l = 20; i <= l; i++)
|
|
29
|
+
console.log(`Selected page ${i}:`, pagination(i, l));
|
|
30
|
+
|
|
31
|
+
Expected output:
|
|
32
|
+
|
|
33
|
+
Selected page 1: [1, 2, 3, "...", 20]
|
|
34
|
+
Selected page 2: [1, 2, 3, 4, "...", 20]
|
|
35
|
+
Selected page 3: [1, 2, 3, 4, 5, "...", 20]
|
|
36
|
+
Selected page 4: [1, 2, 3, 4, 5, 6, "...", 20]
|
|
37
|
+
Selected page 5: [1, 2, 3, 4, 5, 6, 7, "...", 20]
|
|
38
|
+
Selected page 6: [1, "...", 4, 5, 6, 7, 8, "...", 20]
|
|
39
|
+
Selected page 7: [1, "...", 5, 6, 7, 8, 9, "...", 20]
|
|
40
|
+
Selected page 8: [1, "...", 6, 7, 8, 9, 10, "...", 20]
|
|
41
|
+
Selected page 9: [1, "...", 7, 8, 9, 10, 11, "...", 20]
|
|
42
|
+
Selected page 10: [1, "...", 8, 9, 10, 11, 12, "...", 20]
|
|
43
|
+
Selected page 11: [1, "...", 9, 10, 11, 12, 13, "...", 20]
|
|
44
|
+
Selected page 12: [1, "...", 10, 11, 12, 13, 14, "...", 20]
|
|
45
|
+
Selected page 13: [1, "...", 11, 12, 13, 14, 15, "...", 20]
|
|
46
|
+
Selected page 14: [1, "...", 12, 13, 14, 15, 16, "...", 20]
|
|
47
|
+
Selected page 15: [1, "...", 13, 14, 15, 16, 17, "...", 20]
|
|
48
|
+
Selected page 16: [1, "...", 14, 15, 16, 17, 18, 19, 20]
|
|
49
|
+
Selected page 17: [1, "...", 15, 16, 17, 18, 19, 20]
|
|
50
|
+
Selected page 18: [1, "...", 16, 17, 18, 19, 20]
|
|
51
|
+
Selected page 19: [1, "...", 17, 18, 19, 20]
|
|
52
|
+
Selected page 20: [1, "...", 18, 19, 20]
|
|
53
|
+
*/
|
package/package.json
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@damillora/iuno",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"scripts": {
|
|
5
|
+
"dev": "vite dev",
|
|
6
|
+
"build": "vite build && npm run prepack",
|
|
7
|
+
"preview": "vite preview",
|
|
8
|
+
"prepare": "svelte-kit sync || echo ''",
|
|
9
|
+
"prepack": "svelte-kit sync && svelte-package && publint",
|
|
10
|
+
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
|
11
|
+
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
|
12
|
+
"lint": "eslint .",
|
|
13
|
+
"storybook": "storybook dev -p 6006",
|
|
14
|
+
"build-storybook": "storybook build"
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"!dist/**/*.test.*",
|
|
19
|
+
"!dist/**/*.spec.*"
|
|
20
|
+
],
|
|
21
|
+
"sideEffects": [
|
|
22
|
+
"**/*.css"
|
|
23
|
+
],
|
|
24
|
+
"svelte": "./dist/index.js",
|
|
25
|
+
"types": "./dist/index.d.ts",
|
|
26
|
+
"type": "module",
|
|
27
|
+
"exports": {
|
|
28
|
+
".": {
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
30
|
+
"svelte": "./dist/index.js"
|
|
31
|
+
},
|
|
32
|
+
"./app.css": "./dist/app.css"
|
|
33
|
+
},
|
|
34
|
+
"peerDependencies": {
|
|
35
|
+
"svelte": "^5.0.0"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@chromatic-com/storybook": "^5.0.1",
|
|
39
|
+
"@eslint/compat": "^2.0.2",
|
|
40
|
+
"@eslint/js": "^9.39.2",
|
|
41
|
+
"@storybook/addon-a11y": "^10.2.10",
|
|
42
|
+
"@storybook/addon-docs": "^10.2.10",
|
|
43
|
+
"@storybook/addon-svelte-csf": "^5.0.11",
|
|
44
|
+
"@storybook/addon-vitest": "^10.2.10",
|
|
45
|
+
"@storybook/sveltekit": "^10.2.10",
|
|
46
|
+
"@sveltejs/adapter-auto": "^7.0.0",
|
|
47
|
+
"@sveltejs/kit": "^2.50.2",
|
|
48
|
+
"@sveltejs/package": "^2.5.7",
|
|
49
|
+
"@sveltejs/vite-plugin-svelte": "^6.2.4",
|
|
50
|
+
"@types/node": "^24",
|
|
51
|
+
"@vitest/browser-playwright": "^4.0.18",
|
|
52
|
+
"@vitest/coverage-v8": "^4.0.18",
|
|
53
|
+
"eslint": "^9.39.2",
|
|
54
|
+
"eslint-plugin-storybook": "^10.2.10",
|
|
55
|
+
"eslint-plugin-svelte": "^3.14.0",
|
|
56
|
+
"globals": "^17.3.0",
|
|
57
|
+
"playwright": "^1.58.2",
|
|
58
|
+
"publint": "^0.3.17",
|
|
59
|
+
"storybook": "^10.2.10",
|
|
60
|
+
"svelte": "^5.49.2",
|
|
61
|
+
"svelte-check": "^4.3.6",
|
|
62
|
+
"typescript": "^5.9.3",
|
|
63
|
+
"typescript-eslint": "^8.54.0",
|
|
64
|
+
"vite": "^7.3.1",
|
|
65
|
+
"vitest": "^4.0.18",
|
|
66
|
+
"@tailwindcss/vite": "^4.1.14",
|
|
67
|
+
"tailwindcss": "^4.1.14"
|
|
68
|
+
},
|
|
69
|
+
"keywords": [
|
|
70
|
+
"svelte"
|
|
71
|
+
]
|
|
72
|
+
}
|