@aryagg/layout-kit 0.0.2
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 +77 -0
- package/dist/header/Header-1.svelte +65 -0
- package/dist/header/Header-1.svelte.d.ts +13 -0
- package/dist/header/Header-2.svelte +67 -0
- package/dist/header/Header-2.svelte.d.ts +13 -0
- package/dist/header/Header-3.svelte +68 -0
- package/dist/header/Header-3.svelte.d.ts +13 -0
- package/dist/header/Header-4.svelte +42 -0
- package/dist/header/Header-4.svelte.d.ts +12 -0
- package/dist/header/Header.svelte +236 -0
- package/dist/header/Header.svelte.d.ts +19 -0
- package/dist/header/_NavList.svelte +94 -0
- package/dist/header/_NavList.svelte.d.ts +11 -0
- package/dist/header/index.d.ts +5 -0
- package/dist/header/index.js +4 -0
- package/dist/header/types.d.ts +18 -0
- package/dist/header/types.js +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +2 -0
- package/dist/landing-pages/Landingpage-1.svelte +86 -0
- package/dist/landing-pages/Landingpage-1.svelte.d.ts +19 -0
- package/dist/landing-pages/Landingpage-2.svelte +57 -0
- package/dist/landing-pages/Landingpage-2.svelte.d.ts +11 -0
- package/dist/landing-pages/Landingpage-3.svelte +73 -0
- package/dist/landing-pages/Landingpage-3.svelte.d.ts +18 -0
- package/dist/landing-pages/index.d.ts +3 -0
- package/dist/landing-pages/index.js +3 -0
- package/package.json +64 -0
package/README.md
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# ui-kit
|
|
2
|
+
|
|
3
|
+
A Svelte 5 component library template powered by [SvelteKit](https://svelte.dev/docs/kit), [Tailwind CSS v4](https://tailwindcss.com), and TypeScript.
|
|
4
|
+
|
|
5
|
+
## Stack
|
|
6
|
+
|
|
7
|
+
- **Svelte 5** with runes mode enforced across the project
|
|
8
|
+
- **SvelteKit** — dev server, routing, and packaging
|
|
9
|
+
- **Tailwind CSS v4** via `@tailwindcss/vite`
|
|
10
|
+
- **TypeScript** with strict config
|
|
11
|
+
- **ESLint + Prettier** (with `eslint-plugin-svelte`)
|
|
12
|
+
- **`@aryagg/theme`**, **`@aryagg/types`**, **`@aryagg/utils`** — shared design system packages
|
|
13
|
+
|
|
14
|
+
## Getting started
|
|
15
|
+
|
|
16
|
+
Install dependencies:
|
|
17
|
+
|
|
18
|
+
```sh
|
|
19
|
+
npm install
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Start the dev server (includes a live showcase app at `src/routes`):
|
|
23
|
+
|
|
24
|
+
```sh
|
|
25
|
+
npm run dev
|
|
26
|
+
|
|
27
|
+
# open in browser automatically
|
|
28
|
+
npm run dev -- --open
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Project structure
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
src/
|
|
35
|
+
lib/ ← library source (exported to consumers)
|
|
36
|
+
routes/ ← showcase / preview app (not published)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Everything inside `src/lib` is part of the published library. Use `src/routes` to build demos and test components interactively.
|
|
40
|
+
|
|
41
|
+
## Building
|
|
42
|
+
|
|
43
|
+
Build and package the library:
|
|
44
|
+
|
|
45
|
+
```sh
|
|
46
|
+
npm run build
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
This runs `svelte-kit sync`, `svelte-package`, and `publint` in sequence. Output goes to `dist/`.
|
|
50
|
+
|
|
51
|
+
Preview the showcase app production build:
|
|
52
|
+
|
|
53
|
+
```sh
|
|
54
|
+
npm run preview
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Type checking & linting
|
|
58
|
+
|
|
59
|
+
```sh
|
|
60
|
+
npm run check # svelte-check + tsc
|
|
61
|
+
npm run check:watch # watch mode
|
|
62
|
+
|
|
63
|
+
npm run lint # prettier + eslint
|
|
64
|
+
npm run format # auto-format with prettier
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Publishing
|
|
68
|
+
|
|
69
|
+
1. Set the `"name"` field in `package.json` to your desired package name.
|
|
70
|
+
2. Add a `"license"` field and a `LICENSE` file (e.g. [MIT](https://opensource.org/license/mit/)).
|
|
71
|
+
3. Publish to npm:
|
|
72
|
+
|
|
73
|
+
```sh
|
|
74
|
+
npm publish
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
The `"exports"` field in `package.json` exposes `./dist/index.js` (Svelte) and `./dist/index.d.ts` (types) for consumers.
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import type { NavItem } from './types';
|
|
4
|
+
import NavList from './_NavList.svelte';
|
|
5
|
+
|
|
6
|
+
let {
|
|
7
|
+
brand = 'Brand',
|
|
8
|
+
brandHref = '/',
|
|
9
|
+
brandIcon,
|
|
10
|
+
items = [],
|
|
11
|
+
actions,
|
|
12
|
+
class: klass = '',
|
|
13
|
+
}: {
|
|
14
|
+
brand?: string;
|
|
15
|
+
brandHref?: string;
|
|
16
|
+
brandIcon?: any;
|
|
17
|
+
items?: NavItem[];
|
|
18
|
+
actions?: Snippet;
|
|
19
|
+
class?: string;
|
|
20
|
+
} = $props();
|
|
21
|
+
|
|
22
|
+
let headerEl: HTMLElement;
|
|
23
|
+
let openL1 = $state<number | null>(null);
|
|
24
|
+
let openL2 = $state<number | null>(null);
|
|
25
|
+
|
|
26
|
+
function toggleL1(i: number) {
|
|
27
|
+
openL1 = openL1 === i ? null : i;
|
|
28
|
+
openL2 = null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function toggleL2(i: number) {
|
|
32
|
+
openL2 = openL2 === i ? null : i;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function onWindowClick(e: MouseEvent) {
|
|
36
|
+
if (!headerEl?.contains(e.target as Node)) {
|
|
37
|
+
openL1 = null;
|
|
38
|
+
openL2 = null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
</script>
|
|
42
|
+
|
|
43
|
+
<!-- Header-1: brand left · nav left · actions right -->
|
|
44
|
+
|
|
45
|
+
<svelte:window onclick={onWindowClick} />
|
|
46
|
+
|
|
47
|
+
<header
|
|
48
|
+
bind:this={headerEl}
|
|
49
|
+
class="sticky top-0 z-50 w-full border-b border-border-primary bg-surface-primary {klass}"
|
|
50
|
+
>
|
|
51
|
+
<div class="mx-auto flex h-14 max-w-7xl items-center gap-4 px-4">
|
|
52
|
+
|
|
53
|
+
<a href={brandHref} class="flex shrink-0 items-center gap-2 no-underline opacity-100 hover:opacity-80">
|
|
54
|
+
{#if brandIcon}{@const Icon = brandIcon}<Icon class="size-5 text-accent" />{/if}
|
|
55
|
+
<span class="text-sm font-semibold text-primary">{brand}</span>
|
|
56
|
+
</a>
|
|
57
|
+
|
|
58
|
+
<NavList {items} {openL1} {openL2} onToggleL1={toggleL1} onToggleL2={toggleL2} />
|
|
59
|
+
|
|
60
|
+
{#if actions}
|
|
61
|
+
<div class="ml-auto flex items-center gap-2">{@render actions()}</div>
|
|
62
|
+
{/if}
|
|
63
|
+
|
|
64
|
+
</div>
|
|
65
|
+
</header>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { NavItem } from './types';
|
|
3
|
+
type $$ComponentProps = {
|
|
4
|
+
brand?: string;
|
|
5
|
+
brandHref?: string;
|
|
6
|
+
brandIcon?: any;
|
|
7
|
+
items?: NavItem[];
|
|
8
|
+
actions?: Snippet;
|
|
9
|
+
class?: string;
|
|
10
|
+
};
|
|
11
|
+
declare const Header1: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
12
|
+
type Header1 = ReturnType<typeof Header1>;
|
|
13
|
+
export default Header1;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import type { NavItem } from './types';
|
|
4
|
+
import NavList from './_NavList.svelte';
|
|
5
|
+
|
|
6
|
+
let {
|
|
7
|
+
brand = 'Brand',
|
|
8
|
+
brandHref = '/',
|
|
9
|
+
brandIcon,
|
|
10
|
+
items = [],
|
|
11
|
+
actions,
|
|
12
|
+
class: klass = '',
|
|
13
|
+
}: {
|
|
14
|
+
brand?: string;
|
|
15
|
+
brandHref?: string;
|
|
16
|
+
brandIcon?: any;
|
|
17
|
+
items?: NavItem[];
|
|
18
|
+
actions?: Snippet;
|
|
19
|
+
class?: string;
|
|
20
|
+
} = $props();
|
|
21
|
+
|
|
22
|
+
let headerEl: HTMLElement;
|
|
23
|
+
let openL1 = $state<number | null>(null);
|
|
24
|
+
let openL2 = $state<number | null>(null);
|
|
25
|
+
|
|
26
|
+
function toggleL1(i: number) {
|
|
27
|
+
openL1 = openL1 === i ? null : i;
|
|
28
|
+
openL2 = null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function toggleL2(i: number) {
|
|
32
|
+
openL2 = openL2 === i ? null : i;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function onWindowClick(e: MouseEvent) {
|
|
36
|
+
if (!headerEl?.contains(e.target as Node)) {
|
|
37
|
+
openL1 = null;
|
|
38
|
+
openL2 = null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
</script>
|
|
42
|
+
|
|
43
|
+
<!-- Header-2: brand left · nav grid-centered · actions right -->
|
|
44
|
+
|
|
45
|
+
<svelte:window onclick={onWindowClick} />
|
|
46
|
+
|
|
47
|
+
<header
|
|
48
|
+
bind:this={headerEl}
|
|
49
|
+
class="sticky top-0 z-50 w-full border-b border-border-primary bg-surface-primary {klass}"
|
|
50
|
+
>
|
|
51
|
+
<div class="mx-auto grid h-14 max-w-7xl grid-cols-[auto_1fr_auto] items-center gap-4 px-4">
|
|
52
|
+
|
|
53
|
+
<a href={brandHref} class="flex shrink-0 items-center gap-2 no-underline opacity-100 hover:opacity-80">
|
|
54
|
+
{#if brandIcon}{@const Icon = brandIcon}<Icon class="size-5 text-accent" />{/if}
|
|
55
|
+
<span class="text-sm font-semibold text-primary">{brand}</span>
|
|
56
|
+
</a>
|
|
57
|
+
|
|
58
|
+
<div class="flex justify-center">
|
|
59
|
+
<NavList {items} {openL1} {openL2} onToggleL1={toggleL1} onToggleL2={toggleL2} />
|
|
60
|
+
</div>
|
|
61
|
+
|
|
62
|
+
<div class="flex items-center gap-2">
|
|
63
|
+
{#if actions}{@render actions()}{/if}
|
|
64
|
+
</div>
|
|
65
|
+
|
|
66
|
+
</div>
|
|
67
|
+
</header>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { NavItem } from './types';
|
|
3
|
+
type $$ComponentProps = {
|
|
4
|
+
brand?: string;
|
|
5
|
+
brandHref?: string;
|
|
6
|
+
brandIcon?: any;
|
|
7
|
+
items?: NavItem[];
|
|
8
|
+
actions?: Snippet;
|
|
9
|
+
class?: string;
|
|
10
|
+
};
|
|
11
|
+
declare const Header2: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
12
|
+
type Header2 = ReturnType<typeof Header2>;
|
|
13
|
+
export default Header2;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import type { NavItem } from './types';
|
|
4
|
+
import NavList from './_NavList.svelte';
|
|
5
|
+
|
|
6
|
+
let {
|
|
7
|
+
brand = 'Brand',
|
|
8
|
+
brandHref = '/',
|
|
9
|
+
brandIcon,
|
|
10
|
+
items = [],
|
|
11
|
+
actions,
|
|
12
|
+
class: klass = '',
|
|
13
|
+
}: {
|
|
14
|
+
brand?: string;
|
|
15
|
+
brandHref?: string;
|
|
16
|
+
brandIcon?: any;
|
|
17
|
+
items?: NavItem[];
|
|
18
|
+
actions?: Snippet;
|
|
19
|
+
class?: string;
|
|
20
|
+
} = $props();
|
|
21
|
+
|
|
22
|
+
let headerEl: HTMLElement;
|
|
23
|
+
let openL1 = $state<number | null>(null);
|
|
24
|
+
let openL2 = $state<number | null>(null);
|
|
25
|
+
|
|
26
|
+
function toggleL1(i: number) {
|
|
27
|
+
openL1 = openL1 === i ? null : i;
|
|
28
|
+
openL2 = null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function toggleL2(i: number) {
|
|
32
|
+
openL2 = openL2 === i ? null : i;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function onWindowClick(e: MouseEvent) {
|
|
36
|
+
if (!headerEl?.contains(e.target as Node)) {
|
|
37
|
+
openL1 = null;
|
|
38
|
+
openL2 = null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
</script>
|
|
42
|
+
|
|
43
|
+
<!-- Header-3: row 1 brand + actions · row 2 nav centered -->
|
|
44
|
+
|
|
45
|
+
<svelte:window onclick={onWindowClick} />
|
|
46
|
+
|
|
47
|
+
<header
|
|
48
|
+
bind:this={headerEl}
|
|
49
|
+
class="sticky top-0 z-50 w-full border-b border-border-primary bg-surface-primary {klass}"
|
|
50
|
+
>
|
|
51
|
+
<!-- row 1 -->
|
|
52
|
+
<div class="mx-auto flex h-12 max-w-7xl items-center justify-between px-4">
|
|
53
|
+
<a href={brandHref} class="flex shrink-0 items-center gap-2 no-underline opacity-100 hover:opacity-80">
|
|
54
|
+
{#if brandIcon}{@const Icon = brandIcon}<Icon class="size-5 text-accent" />{/if}
|
|
55
|
+
<span class="text-sm font-semibold text-primary">{brand}</span>
|
|
56
|
+
</a>
|
|
57
|
+
{#if actions}
|
|
58
|
+
<div class="flex items-center gap-2">{@render actions()}</div>
|
|
59
|
+
{/if}
|
|
60
|
+
</div>
|
|
61
|
+
|
|
62
|
+
<!-- row 2 -->
|
|
63
|
+
<div class="border-t border-border-primary">
|
|
64
|
+
<div class="mx-auto flex h-10 max-w-7xl items-center justify-center px-4">
|
|
65
|
+
<NavList {items} {openL1} {openL2} onToggleL1={toggleL1} onToggleL2={toggleL2} />
|
|
66
|
+
</div>
|
|
67
|
+
</div>
|
|
68
|
+
</header>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { NavItem } from './types';
|
|
3
|
+
type $$ComponentProps = {
|
|
4
|
+
brand?: string;
|
|
5
|
+
brandHref?: string;
|
|
6
|
+
brandIcon?: any;
|
|
7
|
+
items?: NavItem[];
|
|
8
|
+
actions?: Snippet;
|
|
9
|
+
class?: string;
|
|
10
|
+
};
|
|
11
|
+
declare const Header3: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
12
|
+
type Header3 = ReturnType<typeof Header3>;
|
|
13
|
+
export default Header3;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
|
|
4
|
+
let {
|
|
5
|
+
brand = 'Brand',
|
|
6
|
+
brandHref = '/',
|
|
7
|
+
brandIcon,
|
|
8
|
+
tagline = '',
|
|
9
|
+
actions,
|
|
10
|
+
class: klass = '',
|
|
11
|
+
}: {
|
|
12
|
+
brand?: string;
|
|
13
|
+
brandHref?: string;
|
|
14
|
+
brandIcon?: any;
|
|
15
|
+
tagline?: string;
|
|
16
|
+
actions?: Snippet;
|
|
17
|
+
class?: string;
|
|
18
|
+
} = $props();
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
<!-- Header-4: minimal — brand left · tagline center · actions right -->
|
|
22
|
+
|
|
23
|
+
<header class="sticky top-0 z-50 w-full border-b border-border-primary bg-surface-primary {klass}">
|
|
24
|
+
<div class="mx-auto grid h-14 max-w-7xl grid-cols-[auto_1fr_auto] items-center gap-4 px-4">
|
|
25
|
+
|
|
26
|
+
<a href={brandHref} class="flex shrink-0 items-center gap-2 no-underline opacity-100 hover:opacity-80">
|
|
27
|
+
{#if brandIcon}{@const Icon = brandIcon}<Icon class="size-5 text-accent" />{/if}
|
|
28
|
+
<span class="text-sm font-semibold text-primary">{brand}</span>
|
|
29
|
+
</a>
|
|
30
|
+
|
|
31
|
+
{#if tagline}
|
|
32
|
+
<p class="text-center text-sm text-tertiary">{tagline}</p>
|
|
33
|
+
{:else}
|
|
34
|
+
<span></span>
|
|
35
|
+
{/if}
|
|
36
|
+
|
|
37
|
+
<div class="flex items-center gap-2">
|
|
38
|
+
{#if actions}{@render actions()}{/if}
|
|
39
|
+
</div>
|
|
40
|
+
|
|
41
|
+
</div>
|
|
42
|
+
</header>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
brand?: string;
|
|
4
|
+
brandHref?: string;
|
|
5
|
+
brandIcon?: any;
|
|
6
|
+
tagline?: string;
|
|
7
|
+
actions?: Snippet;
|
|
8
|
+
class?: string;
|
|
9
|
+
};
|
|
10
|
+
declare const Header4: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
11
|
+
type Header4 = ReturnType<typeof Header4>;
|
|
12
|
+
export default Header4;
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import type { NavItem } from './types.js';
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
brand,
|
|
7
|
+
brandHref = '/',
|
|
8
|
+
brandIcon,
|
|
9
|
+
items = [],
|
|
10
|
+
actions,
|
|
11
|
+
variant = 'default',
|
|
12
|
+
class: klass = '',
|
|
13
|
+
}: {
|
|
14
|
+
brand: string;
|
|
15
|
+
brandHref?: string;
|
|
16
|
+
brandIcon?: any;
|
|
17
|
+
items?: NavItem[];
|
|
18
|
+
actions?: Snippet;
|
|
19
|
+
/** default — brand left · nav left · actions right
|
|
20
|
+
* centered — brand left · nav grid-centered · actions right
|
|
21
|
+
* stacked — row 1: brand + actions | row 2: nav centered
|
|
22
|
+
* minimal — brand left · actions right · no nav
|
|
23
|
+
*/
|
|
24
|
+
variant?: 'default' | 'centered' | 'stacked' | 'minimal';
|
|
25
|
+
class?: string;
|
|
26
|
+
} = $props();
|
|
27
|
+
|
|
28
|
+
let headerEl: HTMLElement;
|
|
29
|
+
let openL1 = $state<number | null>(null);
|
|
30
|
+
let openL2 = $state<number | null>(null);
|
|
31
|
+
|
|
32
|
+
function toggleL1(i: number) {
|
|
33
|
+
openL1 = openL1 === i ? null : i;
|
|
34
|
+
if (openL1 !== i) openL2 = null;
|
|
35
|
+
else openL2 = null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function toggleL2(i: number) {
|
|
39
|
+
openL2 = openL2 === i ? null : i;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function handleWindowClick(e: MouseEvent) {
|
|
43
|
+
if (!headerEl?.contains(e.target as Node)) {
|
|
44
|
+
openL1 = null;
|
|
45
|
+
openL2 = null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
</script>
|
|
49
|
+
|
|
50
|
+
<!-- ─── reusable snippets ────────────────────────────────────── -->
|
|
51
|
+
|
|
52
|
+
{#snippet brandMark()}
|
|
53
|
+
<a href={brandHref} class="flex shrink-0 items-center gap-2 no-underline opacity-100 hover:opacity-80">
|
|
54
|
+
{#if brandIcon}
|
|
55
|
+
{@const BrandIcon = brandIcon}
|
|
56
|
+
<BrandIcon class="size-5 text-accent" />
|
|
57
|
+
{/if}
|
|
58
|
+
<span class="text-sm font-semibold text-primary">{brand}</span>
|
|
59
|
+
</a>
|
|
60
|
+
{/snippet}
|
|
61
|
+
|
|
62
|
+
{#snippet navList()}
|
|
63
|
+
<nav class="flex items-center gap-0.5">
|
|
64
|
+
{#each items as item, i}
|
|
65
|
+
<div class="relative">
|
|
66
|
+
{#if item.children?.length}
|
|
67
|
+
<!-- L1 trigger -->
|
|
68
|
+
<button
|
|
69
|
+
class="btn-ghost btn-sm flex items-center gap-1.5"
|
|
70
|
+
onclick={() => toggleL1(i)}
|
|
71
|
+
aria-expanded={openL1 === i}
|
|
72
|
+
aria-haspopup="true"
|
|
73
|
+
>
|
|
74
|
+
{#if item.icon}
|
|
75
|
+
{@const L1Icon = item.icon}
|
|
76
|
+
<L1Icon class="size-4 shrink-0" />
|
|
77
|
+
{/if}
|
|
78
|
+
<span>{item.label}</span>
|
|
79
|
+
{#if item.badge}
|
|
80
|
+
<span data-badge>{item.badge}</span>
|
|
81
|
+
{/if}
|
|
82
|
+
<svg
|
|
83
|
+
class="size-3 transition-transform duration-150 {openL1 === i ? 'rotate-180' : ''}"
|
|
84
|
+
viewBox="0 0 16 16" fill="none" stroke="currentColor"
|
|
85
|
+
stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"
|
|
86
|
+
>
|
|
87
|
+
<path d="M4 6l4 4 4-4" />
|
|
88
|
+
</svg>
|
|
89
|
+
</button>
|
|
90
|
+
|
|
91
|
+
<!-- L2 dropdown -->
|
|
92
|
+
{#if openL1 === i}
|
|
93
|
+
<div class="absolute left-0 top-full z-50 mt-1.5 min-w-52 rounded-2xl border border-border-primary bg-surface-primary p-1 shadow-lg">
|
|
94
|
+
{#each item.children as child, ci}
|
|
95
|
+
{#if child.children?.length}
|
|
96
|
+
<!-- L2 trigger + L3 flyout -->
|
|
97
|
+
<div class="relative">
|
|
98
|
+
<button
|
|
99
|
+
class="flex w-full items-center gap-2 rounded-xl border-transparent bg-transparent px-3 py-2 text-sm text-secondary transition-colors hover:bg-surface-secondary hover:text-primary"
|
|
100
|
+
onclick={() => toggleL2(ci)}
|
|
101
|
+
aria-expanded={openL2 === ci}
|
|
102
|
+
aria-haspopup="true"
|
|
103
|
+
>
|
|
104
|
+
{#if child.icon}
|
|
105
|
+
{@const L2Icon = child.icon}
|
|
106
|
+
<L2Icon class="size-4 shrink-0 text-tertiary" />
|
|
107
|
+
{/if}
|
|
108
|
+
<span class="flex-1 text-left">{child.label}</span>
|
|
109
|
+
{#if child.badge}
|
|
110
|
+
<span data-badge class="ml-auto">{child.badge}</span>
|
|
111
|
+
{/if}
|
|
112
|
+
<svg
|
|
113
|
+
class="size-3 shrink-0 transition-transform duration-150 {openL2 === ci ? 'rotate-90' : ''}"
|
|
114
|
+
viewBox="0 0 16 16" fill="none" stroke="currentColor"
|
|
115
|
+
stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"
|
|
116
|
+
>
|
|
117
|
+
<path d="M6 4l4 4-4 4" />
|
|
118
|
+
</svg>
|
|
119
|
+
</button>
|
|
120
|
+
|
|
121
|
+
<!-- L3 flyout -->
|
|
122
|
+
{#if openL2 === ci}
|
|
123
|
+
<div class="absolute left-full top-0 z-50 ml-1 min-w-48 rounded-2xl border border-border-primary bg-surface-primary p-1 shadow-lg">
|
|
124
|
+
{#each child.children as grandchild}
|
|
125
|
+
<a
|
|
126
|
+
href={grandchild.href ?? '#'}
|
|
127
|
+
class="flex items-center gap-2 rounded-xl px-3 py-2 text-sm text-secondary no-underline transition-colors hover:bg-surface-secondary hover:text-primary"
|
|
128
|
+
>
|
|
129
|
+
{#if grandchild.icon}
|
|
130
|
+
{@const L3Icon = grandchild.icon}
|
|
131
|
+
<L3Icon class="size-4 shrink-0 text-tertiary" />
|
|
132
|
+
{/if}
|
|
133
|
+
<span>{grandchild.label}</span>
|
|
134
|
+
{#if grandchild.badge}
|
|
135
|
+
<span data-badge class="ml-auto">{grandchild.badge}</span>
|
|
136
|
+
{/if}
|
|
137
|
+
</a>
|
|
138
|
+
{/each}
|
|
139
|
+
</div>
|
|
140
|
+
{/if}
|
|
141
|
+
</div>
|
|
142
|
+
{:else}
|
|
143
|
+
<!-- L2 leaf -->
|
|
144
|
+
<a
|
|
145
|
+
href={child.href ?? '#'}
|
|
146
|
+
class="flex items-center gap-2 rounded-xl px-3 py-2 text-sm text-secondary no-underline transition-colors hover:bg-surface-secondary hover:text-primary"
|
|
147
|
+
>
|
|
148
|
+
{#if child.icon}
|
|
149
|
+
{@const L2LeafIcon = child.icon}
|
|
150
|
+
<L2LeafIcon class="size-4 shrink-0 text-tertiary" />
|
|
151
|
+
{/if}
|
|
152
|
+
<span>{child.label}</span>
|
|
153
|
+
{#if child.badge}
|
|
154
|
+
<span data-badge class="ml-auto">{child.badge}</span>
|
|
155
|
+
{/if}
|
|
156
|
+
</a>
|
|
157
|
+
{/if}
|
|
158
|
+
{/each}
|
|
159
|
+
</div>
|
|
160
|
+
{/if}
|
|
161
|
+
{:else}
|
|
162
|
+
<!-- L1 leaf link -->
|
|
163
|
+
<a
|
|
164
|
+
href={item.href ?? '#'}
|
|
165
|
+
class="btn-ghost btn-sm flex items-center gap-1.5 no-underline"
|
|
166
|
+
>
|
|
167
|
+
{#if item.icon}
|
|
168
|
+
{@const L1LeafIcon = item.icon}
|
|
169
|
+
<L1LeafIcon class="size-4 shrink-0" />
|
|
170
|
+
{/if}
|
|
171
|
+
<span>{item.label}</span>
|
|
172
|
+
{#if item.badge}
|
|
173
|
+
<span data-badge>{item.badge}</span>
|
|
174
|
+
{/if}
|
|
175
|
+
</a>
|
|
176
|
+
{/if}
|
|
177
|
+
</div>
|
|
178
|
+
{/each}
|
|
179
|
+
</nav>
|
|
180
|
+
{/snippet}
|
|
181
|
+
|
|
182
|
+
{#snippet actionBar()}
|
|
183
|
+
{#if actions}
|
|
184
|
+
<div class="flex items-center gap-2">
|
|
185
|
+
{@render actions()}
|
|
186
|
+
</div>
|
|
187
|
+
{/if}
|
|
188
|
+
{/snippet}
|
|
189
|
+
|
|
190
|
+
<!-- ─── variants ─────────────────────────────────────────────── -->
|
|
191
|
+
|
|
192
|
+
<svelte:window onclick={handleWindowClick} />
|
|
193
|
+
|
|
194
|
+
<header
|
|
195
|
+
bind:this={headerEl}
|
|
196
|
+
class="sticky top-0 z-50 w-full border-b border-border-primary bg-surface-primary {klass}"
|
|
197
|
+
>
|
|
198
|
+
|
|
199
|
+
{#if variant === 'default'}
|
|
200
|
+
<!-- brand · nav · ··· · actions -->
|
|
201
|
+
<div class="mx-auto flex h-14 max-w-7xl items-center gap-4 px-4">
|
|
202
|
+
{@render brandMark()}
|
|
203
|
+
{@render navList()}
|
|
204
|
+
<div class="ml-auto">{@render actionBar()}</div>
|
|
205
|
+
</div>
|
|
206
|
+
|
|
207
|
+
{:else if variant === 'centered'}
|
|
208
|
+
<!-- brand · ··· · [nav centered] · ··· · actions -->
|
|
209
|
+
<div class="mx-auto grid h-14 max-w-7xl grid-cols-[auto_1fr_auto] items-center gap-4 px-4">
|
|
210
|
+
{@render brandMark()}
|
|
211
|
+
<div class="flex justify-center">{@render navList()}</div>
|
|
212
|
+
{@render actionBar()}
|
|
213
|
+
</div>
|
|
214
|
+
|
|
215
|
+
{:else if variant === 'stacked'}
|
|
216
|
+
<!-- row 1: brand ··· actions -->
|
|
217
|
+
<div class="mx-auto flex h-12 max-w-7xl items-center justify-between px-4">
|
|
218
|
+
{@render brandMark()}
|
|
219
|
+
{@render actionBar()}
|
|
220
|
+
</div>
|
|
221
|
+
<!-- row 2: nav centered -->
|
|
222
|
+
<div class="border-t border-border-primary">
|
|
223
|
+
<div class="mx-auto flex h-10 max-w-7xl items-center justify-center px-4">
|
|
224
|
+
{@render navList()}
|
|
225
|
+
</div>
|
|
226
|
+
</div>
|
|
227
|
+
|
|
228
|
+
{:else if variant === 'minimal'}
|
|
229
|
+
<!-- brand ··· actions — no nav -->
|
|
230
|
+
<div class="mx-auto flex h-14 max-w-7xl items-center justify-between px-4">
|
|
231
|
+
{@render brandMark()}
|
|
232
|
+
{@render actionBar()}
|
|
233
|
+
</div>
|
|
234
|
+
{/if}
|
|
235
|
+
|
|
236
|
+
</header>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { NavItem } from './types.js';
|
|
3
|
+
type $$ComponentProps = {
|
|
4
|
+
brand: string;
|
|
5
|
+
brandHref?: string;
|
|
6
|
+
brandIcon?: any;
|
|
7
|
+
items?: NavItem[];
|
|
8
|
+
actions?: Snippet;
|
|
9
|
+
/** default — brand left · nav left · actions right
|
|
10
|
+
* centered — brand left · nav grid-centered · actions right
|
|
11
|
+
* stacked — row 1: brand + actions | row 2: nav centered
|
|
12
|
+
* minimal — brand left · actions right · no nav
|
|
13
|
+
*/
|
|
14
|
+
variant?: 'default' | 'centered' | 'stacked' | 'minimal';
|
|
15
|
+
class?: string;
|
|
16
|
+
};
|
|
17
|
+
declare const Header: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
18
|
+
type Header = ReturnType<typeof Header>;
|
|
19
|
+
export default Header;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { resolve } from '$app/paths';
|
|
3
|
+
import type { NavItem } from './types';
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
items,
|
|
7
|
+
openL1,
|
|
8
|
+
openL2,
|
|
9
|
+
onToggleL1,
|
|
10
|
+
onToggleL2,
|
|
11
|
+
}: {
|
|
12
|
+
items: NavItem[];
|
|
13
|
+
openL1: number | null;
|
|
14
|
+
openL2: number | null;
|
|
15
|
+
onToggleL1: (i: number) => void;
|
|
16
|
+
onToggleL2: (i: number) => void;
|
|
17
|
+
} = $props();
|
|
18
|
+
</script>
|
|
19
|
+
|
|
20
|
+
<nav class="flex items-center gap-0.5">
|
|
21
|
+
{#each items as item, i (i)}
|
|
22
|
+
<div class="relative">
|
|
23
|
+
{#if item.children?.length}
|
|
24
|
+
<button
|
|
25
|
+
class="btn-ghost btn-sm flex items-center gap-1.5"
|
|
26
|
+
onclick={() => onToggleL1(i)}
|
|
27
|
+
aria-expanded={openL1 === i}
|
|
28
|
+
aria-haspopup="true"
|
|
29
|
+
>
|
|
30
|
+
{#if item.icon}{@const I = item.icon}<I class="size-4 shrink-0" />{/if}
|
|
31
|
+
<span>{item.label}</span>
|
|
32
|
+
{#if item.badge}<span data-badge>{item.badge}</span>{/if}
|
|
33
|
+
<svg class="size-3 transition-transform duration-150 {openL1 === i ? 'rotate-180' : ''}"
|
|
34
|
+
viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.5"
|
|
35
|
+
stroke-linecap="round" stroke-linejoin="round">
|
|
36
|
+
<path d="M4 6l4 4 4-4" />
|
|
37
|
+
</svg>
|
|
38
|
+
</button>
|
|
39
|
+
|
|
40
|
+
{#if openL1 === i}
|
|
41
|
+
<div class="absolute left-0 top-full z-50 mt-1.5 min-w-52 rounded-2xl border border-border-primary bg-surface-primary p-1 shadow-lg">
|
|
42
|
+
{#each item.children as child, ci (ci)}
|
|
43
|
+
{#if child.children?.length}
|
|
44
|
+
<div class="relative">
|
|
45
|
+
<button
|
|
46
|
+
class="flex w-full items-center gap-2 rounded-xl border-transparent bg-transparent px-3 py-2 text-sm text-secondary transition-colors hover:bg-surface-secondary hover:text-primary"
|
|
47
|
+
onclick={() => onToggleL2(ci)}
|
|
48
|
+
aria-expanded={openL2 === ci}
|
|
49
|
+
aria-haspopup="true"
|
|
50
|
+
>
|
|
51
|
+
{#if child.icon}{@const I = child.icon}<I class="size-4 shrink-0 text-tertiary" />{/if}
|
|
52
|
+
<span class="flex-1 text-left">{child.label}</span>
|
|
53
|
+
{#if child.badge}<span data-badge class="ml-auto">{child.badge}</span>{/if}
|
|
54
|
+
<svg class="size-3 shrink-0 transition-transform duration-150 {openL2 === ci ? 'rotate-90' : ''}"
|
|
55
|
+
viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.5"
|
|
56
|
+
stroke-linecap="round" stroke-linejoin="round">
|
|
57
|
+
<path d="M6 4l4 4-4 4" />
|
|
58
|
+
</svg>
|
|
59
|
+
</button>
|
|
60
|
+
|
|
61
|
+
{#if openL2 === ci}
|
|
62
|
+
<div class="absolute left-full top-0 z-50 ml-1 min-w-48 rounded-2xl border border-border-primary bg-surface-primary p-1 shadow-lg">
|
|
63
|
+
{#each child.children as grandchild,i (i)}
|
|
64
|
+
<a href={resolve(grandchild.href ?? '#',{})}
|
|
65
|
+
class="flex items-center gap-2 rounded-xl px-3 py-2 text-sm text-secondary no-underline transition-colors hover:bg-surface-secondary hover:text-primary">
|
|
66
|
+
{#if grandchild.icon}{@const I = grandchild.icon}<I class="size-4 shrink-0 text-tertiary" />{/if}
|
|
67
|
+
<span>{grandchild.label}</span>
|
|
68
|
+
{#if grandchild.badge}<span data-badge class="ml-auto">{grandchild.badge}</span>{/if}
|
|
69
|
+
</a>
|
|
70
|
+
{/each}
|
|
71
|
+
</div>
|
|
72
|
+
{/if}
|
|
73
|
+
</div>
|
|
74
|
+
{:else}
|
|
75
|
+
<a href={child.href ?? '#'}
|
|
76
|
+
class="flex items-center gap-2 rounded-xl px-3 py-2 text-sm text-secondary no-underline transition-colors hover:bg-surface-secondary hover:text-primary">
|
|
77
|
+
{#if child.icon}{@const I = child.icon}<I class="size-4 shrink-0 text-tertiary" />{/if}
|
|
78
|
+
<span>{child.label}</span>
|
|
79
|
+
{#if child.badge}<span data-badge class="ml-auto">{child.badge}</span>{/if}
|
|
80
|
+
</a>
|
|
81
|
+
{/if}
|
|
82
|
+
{/each}
|
|
83
|
+
</div>
|
|
84
|
+
{/if}
|
|
85
|
+
{:else}
|
|
86
|
+
<a href={item.href ?? '#'} class="btn-ghost btn-sm flex items-center gap-1.5 no-underline">
|
|
87
|
+
{#if item.icon}{@const I = item.icon}<I class="size-4 shrink-0" />{/if}
|
|
88
|
+
<span>{item.label}</span>
|
|
89
|
+
{#if item.badge}<span data-badge>{item.badge}</span>{/if}
|
|
90
|
+
</a>
|
|
91
|
+
{/if}
|
|
92
|
+
</div>
|
|
93
|
+
{/each}
|
|
94
|
+
</nav>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { NavItem } from './types';
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
items: NavItem[];
|
|
4
|
+
openL1: number | null;
|
|
5
|
+
openL2: number | null;
|
|
6
|
+
onToggleL1: (i: number) => void;
|
|
7
|
+
onToggleL2: (i: number) => void;
|
|
8
|
+
};
|
|
9
|
+
declare const NavList: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
10
|
+
type NavList = ReturnType<typeof NavList>;
|
|
11
|
+
export default NavList;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
export type NavItem = {
|
|
3
|
+
label: string;
|
|
4
|
+
href?: string;
|
|
5
|
+
icon?: any;
|
|
6
|
+
badge?: string;
|
|
7
|
+
children?: NavItem[];
|
|
8
|
+
};
|
|
9
|
+
export type HeaderVariant = 'default' | 'centered' | 'stacked' | 'minimal';
|
|
10
|
+
export type HeaderProps = {
|
|
11
|
+
brand: string;
|
|
12
|
+
brandHref?: string;
|
|
13
|
+
brandIcon?: any;
|
|
14
|
+
items?: NavItem[];
|
|
15
|
+
actions?: Snippet;
|
|
16
|
+
variant?: HeaderVariant;
|
|
17
|
+
class?: string;
|
|
18
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from "svelte";
|
|
3
|
+
|
|
4
|
+
let {
|
|
5
|
+
title = 'Greeny',
|
|
6
|
+
logo = 'https://image.flaticon.com/icons/svg/497/497348.svg',
|
|
7
|
+
heading = 'Find your greeny stuff for your room',
|
|
8
|
+
highlight = 'greeny',
|
|
9
|
+
description = `Lorem ipsum dolor sit amet consectetur adipisicing elit.
|
|
10
|
+
Recusandae maiores neque eaque ea odit placeat, tenetur illum
|
|
11
|
+
distinctio nulla voluptatum a corrupti beatae tempora aperiam
|
|
12
|
+
quia id aliquam possimus aut.`,
|
|
13
|
+
ctaText = 'Learn More',
|
|
14
|
+
image = 'https://images.unsplash.com/photo-1536147116438-62679a5e01f2?auto=format&fit=crop&w=634&q=80',
|
|
15
|
+
imageAlt = 'Product showcase',
|
|
16
|
+
onCta,
|
|
17
|
+
lSnippet,
|
|
18
|
+
value = $bindable('Wooooooooooeeeeee')
|
|
19
|
+
}: {
|
|
20
|
+
title?: string;
|
|
21
|
+
logo?: string;
|
|
22
|
+
heading?: string;
|
|
23
|
+
highlight?: string;
|
|
24
|
+
description?: string;
|
|
25
|
+
ctaText?: string;
|
|
26
|
+
image?: string;
|
|
27
|
+
imageAlt?: string;
|
|
28
|
+
onCta?: (msg:string) => void;
|
|
29
|
+
lSnippet?: Snippet;
|
|
30
|
+
value?: string
|
|
31
|
+
} = $props();
|
|
32
|
+
|
|
33
|
+
const parts = $derived(highlight && heading.includes(highlight)
|
|
34
|
+
? heading.split(highlight)
|
|
35
|
+
: [heading, '']);
|
|
36
|
+
|
|
37
|
+
export function callFromOutside(ms:any) {
|
|
38
|
+
alert("Hello from outside the component!");
|
|
39
|
+
console.log("Received message:", ms);
|
|
40
|
+
}
|
|
41
|
+
</script>
|
|
42
|
+
|
|
43
|
+
<div class="flex flex-wrap">
|
|
44
|
+
<!-- LEFT SIDE -->
|
|
45
|
+
<div class="w-full sm:w-8/12 mb-10">
|
|
46
|
+
<div class="container mx-auto h-full sm:p-10">
|
|
47
|
+
<!-- NAV -->
|
|
48
|
+
<nav class="flex px-4 justify-between items-center">
|
|
49
|
+
<div class="text-4xl font-bold">
|
|
50
|
+
{title}<span class="text-accent/70">.</span>
|
|
51
|
+
</div>
|
|
52
|
+
<div>
|
|
53
|
+
<img src={logo} alt="{title} logo" class="w-8" />
|
|
54
|
+
</div>
|
|
55
|
+
</nav>
|
|
56
|
+
|
|
57
|
+
<!-- HERO -->
|
|
58
|
+
<header class="container px-4 lg:flex mt-10 items-center h-full lg:mt-0">
|
|
59
|
+
<div class="w-full">
|
|
60
|
+
<h1 class="text-4xl lg:text-6xl font-bold">
|
|
61
|
+
{#if parts[1] !== ''}
|
|
62
|
+
{parts[0]}<span class="text-accent/85">{highlight} <span class="text-amber-800">{value}</span></span>{parts[1]}
|
|
63
|
+
{:else}
|
|
64
|
+
{heading}
|
|
65
|
+
{/if}
|
|
66
|
+
</h1>
|
|
67
|
+
|
|
68
|
+
<div class="w-20 h-2 bg-accent/70 my-4"></div>
|
|
69
|
+
|
|
70
|
+
<p class="text-xl mb-10">{description}</p>
|
|
71
|
+
|
|
72
|
+
{#if lSnippet}
|
|
73
|
+
{@render lSnippet()}
|
|
74
|
+
{/if}
|
|
75
|
+
|
|
76
|
+
<button type="button" class="btn btn-primary" onclick={()=>onCta?.("Hello from Landingpage-1!")}>
|
|
77
|
+
{ctaText}
|
|
78
|
+
</button>
|
|
79
|
+
</div>
|
|
80
|
+
</header>
|
|
81
|
+
</div>
|
|
82
|
+
</div>
|
|
83
|
+
|
|
84
|
+
<!-- RIGHT SIDE IMAGE -->
|
|
85
|
+
<img src={image} alt={imageAlt} class="w-full h-48 object-cover sm:h-screen sm:w-4/12" />
|
|
86
|
+
</div>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Snippet } from "svelte";
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
title?: string;
|
|
4
|
+
logo?: string;
|
|
5
|
+
heading?: string;
|
|
6
|
+
highlight?: string;
|
|
7
|
+
description?: string;
|
|
8
|
+
ctaText?: string;
|
|
9
|
+
image?: string;
|
|
10
|
+
imageAlt?: string;
|
|
11
|
+
onCta?: (msg: string) => void;
|
|
12
|
+
lSnippet?: Snippet;
|
|
13
|
+
value?: string;
|
|
14
|
+
};
|
|
15
|
+
declare const Landingpage1: import("svelte").Component<$$ComponentProps, {
|
|
16
|
+
callFromOutside: (ms: any) => void;
|
|
17
|
+
}, "value">;
|
|
18
|
+
type Landingpage1 = ReturnType<typeof Landingpage1>;
|
|
19
|
+
export default Landingpage1;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
let {
|
|
3
|
+
title = 'Discover Your New Home',
|
|
4
|
+
backgroundImage = 'https://images.unsplash.com/photo-1449844908441-8829872d2607?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w0NzEyNjZ8MHwxfHNlYXJjaHw2fHxob21lfGVufDB8MHx8fDE3MTA0MDE1NDZ8MA&ixlib=rb-4.0.3&q=80&w=1080',
|
|
5
|
+
placeholder = 'City, address, or ZIP',
|
|
6
|
+
buttonText = 'Search',
|
|
7
|
+
inputLabel = 'Search properties',
|
|
8
|
+
onSubmit = (_value: string) => {}
|
|
9
|
+
}: {
|
|
10
|
+
title?: string;
|
|
11
|
+
backgroundImage?: string;
|
|
12
|
+
placeholder?: string;
|
|
13
|
+
buttonText?: string;
|
|
14
|
+
inputLabel?: string;
|
|
15
|
+
onSubmit?: (value: string) => void;
|
|
16
|
+
} = $props();
|
|
17
|
+
|
|
18
|
+
let searchValue = $state('');
|
|
19
|
+
|
|
20
|
+
function handleSubmit(e: SubmitEvent) {
|
|
21
|
+
e.preventDefault();
|
|
22
|
+
onSubmit(searchValue);
|
|
23
|
+
}
|
|
24
|
+
</script>
|
|
25
|
+
|
|
26
|
+
<section class="w-full h-screen">
|
|
27
|
+
<div
|
|
28
|
+
class="w-full h-full opacity-90 bg-cover bg-no-repeat bg-center flex flex-col justify-center items-center"
|
|
29
|
+
style={`background-image: url('${backgroundImage}')`}
|
|
30
|
+
>
|
|
31
|
+
<!-- Title -->
|
|
32
|
+
<h1
|
|
33
|
+
class="text-white text-center xl:text-5xl lg:text-4xl md:text-3xl sm:text-2xl text-xl font-semibold p-2 rounded-sm"
|
|
34
|
+
>
|
|
35
|
+
{title}
|
|
36
|
+
</h1>
|
|
37
|
+
|
|
38
|
+
<!-- Search Form -->
|
|
39
|
+
<div class="w-full mx-auto">
|
|
40
|
+
<form onsubmit={handleSubmit}>
|
|
41
|
+
<div class="xl:w-1/2 lg:w-[60%] sm:w-[70%] w-[90%] mx-auto flex gap-2 md:mt-6 mt-4">
|
|
42
|
+
<label for="lp2-search" class="sr-only">{inputLabel}</label>
|
|
43
|
+
<input
|
|
44
|
+
id="lp2-search"
|
|
45
|
+
type="text"
|
|
46
|
+
bind:value={searchValue}
|
|
47
|
+
class="border w-full p-2 rounded-md text-xl pl-2"
|
|
48
|
+
{placeholder}
|
|
49
|
+
/>
|
|
50
|
+
<button type="submit" class="btn btn-primary">
|
|
51
|
+
{buttonText}
|
|
52
|
+
</button>
|
|
53
|
+
</div>
|
|
54
|
+
</form>
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
57
|
+
</section>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
type $$ComponentProps = {
|
|
2
|
+
title?: string;
|
|
3
|
+
backgroundImage?: string;
|
|
4
|
+
placeholder?: string;
|
|
5
|
+
buttonText?: string;
|
|
6
|
+
inputLabel?: string;
|
|
7
|
+
onSubmit?: (value: string) => void;
|
|
8
|
+
};
|
|
9
|
+
declare const Landingpage2: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
10
|
+
type Landingpage2 = ReturnType<typeof Landingpage2>;
|
|
11
|
+
export default Landingpage2;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Component } from 'svelte';
|
|
3
|
+
import { Heart, Download } from 'svelte-bootstrap-icons';
|
|
4
|
+
|
|
5
|
+
// svelte-bootstrap-icons ships Svelte 4 class components; double-assert to
|
|
6
|
+
// bridge the incompatible Svelte 5 Component function signature.
|
|
7
|
+
type IconComponent = Component<Record<string, never>>;
|
|
8
|
+
|
|
9
|
+
let {
|
|
10
|
+
greeting = 'Hi,',
|
|
11
|
+
role = "I'm Full Stack Developer",
|
|
12
|
+
description = `Lorem ipsum dolor sit, amet consectetur adipisicing elit.
|
|
13
|
+
Aut excepturi magnam enim officiis facilis numquam corporis
|
|
14
|
+
quos accusantium tempora, dolores quod cum facere architecto
|
|
15
|
+
soluta atque corrupti a alias perferendis.`,
|
|
16
|
+
followText = 'Follow',
|
|
17
|
+
resumeText = 'Resume',
|
|
18
|
+
image = 'https://images.pexels.com/photos/415829/pexels-photo-415829.jpeg?auto=compress&cs=tinysrgb&w=600',
|
|
19
|
+
imageAlt = 'Profile photo',
|
|
20
|
+
FollowIcon = Heart as unknown as IconComponent,
|
|
21
|
+
ResumeIcon = Download as unknown as IconComponent,
|
|
22
|
+
onFollow = () => {},
|
|
23
|
+
onResume = () => {}
|
|
24
|
+
}: {
|
|
25
|
+
greeting?: string;
|
|
26
|
+
role?: string;
|
|
27
|
+
description?: string;
|
|
28
|
+
followText?: string;
|
|
29
|
+
resumeText?: string;
|
|
30
|
+
image?: string;
|
|
31
|
+
imageAlt?: string;
|
|
32
|
+
FollowIcon?: IconComponent;
|
|
33
|
+
ResumeIcon?: IconComponent;
|
|
34
|
+
onFollow?: () => void;
|
|
35
|
+
onResume?: () => void;
|
|
36
|
+
} = $props();
|
|
37
|
+
</script>
|
|
38
|
+
|
|
39
|
+
<div class="flex h-screen items-center justify-center bg-surface-secondary p-5">
|
|
40
|
+
<div class="grid md:grid-cols-2 grid-cols-1 items-center gap-10 md:px-10">
|
|
41
|
+
|
|
42
|
+
<!-- LEFT SIDE -->
|
|
43
|
+
<div>
|
|
44
|
+
<h1 class="mb-2 text-3xl font-bold">
|
|
45
|
+
<span class="text-accent">{greeting}</span> {role}
|
|
46
|
+
</h1>
|
|
47
|
+
|
|
48
|
+
<p class="mb-6">{description}</p>
|
|
49
|
+
|
|
50
|
+
<div class="flex justify-center space-x-5">
|
|
51
|
+
<button type="button" class="btn btn-primary w-full" onclick={onFollow}>
|
|
52
|
+
{followText}
|
|
53
|
+
<FollowIcon />
|
|
54
|
+
</button>
|
|
55
|
+
|
|
56
|
+
<button type="button" class="btn btn-secondary w-full" onclick={onResume}>
|
|
57
|
+
{resumeText}
|
|
58
|
+
<ResumeIcon />
|
|
59
|
+
</button>
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
|
|
63
|
+
<!-- RIGHT SIDE IMAGE -->
|
|
64
|
+
<div>
|
|
65
|
+
<img
|
|
66
|
+
src={image}
|
|
67
|
+
alt={imageAlt}
|
|
68
|
+
class="md:size-96 size-72 rounded-full object-cover"
|
|
69
|
+
/>
|
|
70
|
+
</div>
|
|
71
|
+
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Component } from 'svelte';
|
|
2
|
+
type IconComponent = Component<Record<string, never>>;
|
|
3
|
+
type $$ComponentProps = {
|
|
4
|
+
greeting?: string;
|
|
5
|
+
role?: string;
|
|
6
|
+
description?: string;
|
|
7
|
+
followText?: string;
|
|
8
|
+
resumeText?: string;
|
|
9
|
+
image?: string;
|
|
10
|
+
imageAlt?: string;
|
|
11
|
+
FollowIcon?: IconComponent;
|
|
12
|
+
ResumeIcon?: IconComponent;
|
|
13
|
+
onFollow?: () => void;
|
|
14
|
+
onResume?: () => void;
|
|
15
|
+
};
|
|
16
|
+
declare const Landingpage3: Component<$$ComponentProps, {}, "">;
|
|
17
|
+
type Landingpage3 = ReturnType<typeof Landingpage3>;
|
|
18
|
+
export default Landingpage3;
|
package/package.json
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@aryagg/layout-kit",
|
|
3
|
+
"version": "0.0.2",
|
|
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": "prettier --check . && eslint .",
|
|
13
|
+
"format": "prettier --write ."
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"!dist/**/*.test.*",
|
|
18
|
+
"!dist/**/*.spec.*"
|
|
19
|
+
],
|
|
20
|
+
"sideEffects": [
|
|
21
|
+
"**/*.css"
|
|
22
|
+
],
|
|
23
|
+
"svelte": "./dist/index.js",
|
|
24
|
+
"types": "./dist/index.d.ts",
|
|
25
|
+
"type": "module",
|
|
26
|
+
"exports": {
|
|
27
|
+
".": {
|
|
28
|
+
"types": "./dist/index.d.ts",
|
|
29
|
+
"svelte": "./dist/index.js"
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"peerDependencies": {
|
|
33
|
+
"svelte": "^5.0.0"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@aryagg/theme": "^0.0.2",
|
|
37
|
+
"@aryagg/types": "^1.1.1",
|
|
38
|
+
"@eslint/js": "^10.0.1",
|
|
39
|
+
"@sveltejs/adapter-auto": "^7.0.1",
|
|
40
|
+
"@sveltejs/kit": "^2.63.0",
|
|
41
|
+
"@sveltejs/package": "^2.5.8",
|
|
42
|
+
"@sveltejs/vite-plugin-svelte": "^7.1.2",
|
|
43
|
+
"@tailwindcss/vite": "^4.0.0",
|
|
44
|
+
"@types/node": "^22",
|
|
45
|
+
"eslint": "^10.4.1",
|
|
46
|
+
"eslint-config-prettier": "^10.1.8",
|
|
47
|
+
"eslint-plugin-svelte": "^3.19.0",
|
|
48
|
+
"globals": "^17.6.0",
|
|
49
|
+
"prettier": "^3.8.3",
|
|
50
|
+
"prettier-plugin-svelte": "^4.1.0",
|
|
51
|
+
"publint": "^0.3.21",
|
|
52
|
+
"svelte": "^5.56.1",
|
|
53
|
+
"svelte-check": "^4.6.0",
|
|
54
|
+
"typescript": "^6.0.3",
|
|
55
|
+
"typescript-eslint": "^8.60.1",
|
|
56
|
+
"vite": "^8.0.16"
|
|
57
|
+
},
|
|
58
|
+
"keywords": [
|
|
59
|
+
"svelte"
|
|
60
|
+
],
|
|
61
|
+
"dependencies": {
|
|
62
|
+
"@aryagg/utils": "^1.1.2"
|
|
63
|
+
}
|
|
64
|
+
}
|