@levino/shipyard-base 0.1.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.
@@ -0,0 +1,26 @@
1
+ ---
2
+ interface FooterProps {
3
+ links: {
4
+ label: string;
5
+ href: string;
6
+ }[];
7
+ copyright: {
8
+ label: string;
9
+ href: string;
10
+ year: number;
11
+ };
12
+ }
13
+
14
+ const { links, copyright } = Astro.props as FooterProps;
15
+ ---
16
+
17
+ <footer class="flex w-full items-center justify-between px-6 py-4 text-sm font-medium">
18
+ <a href={copyright.href} target="_blank">
19
+ © {copyright.label}, {copyright.year}
20
+ </a>
21
+ {links.map(({ label, href }) => (
22
+ <a href={href}>
23
+ {label}
24
+ </a>
25
+ ))}
26
+ </footer>
@@ -0,0 +1,71 @@
1
+ ---
2
+ import type { Config } from '../schemas/config';
3
+ import { cn } from '../tools/cn';
4
+
5
+ type Props = Pick<Config, 'brand' | 'navigation'> & { showBrand: boolean };
6
+
7
+ const { brand, navigation, showBrand = false } = Astro.props as Props;
8
+ ---
9
+
10
+ <div class="navbar z-10 bg-base-100">
11
+ <span
12
+ class="tooltip tooltip-bottom before:text-xs before:content-[attr(data-tip)]"
13
+ data-tip="Menu"
14
+ >
15
+ <label
16
+ aria-label="Open menu"
17
+ for="drawer"
18
+ class="btn btn-square btn-ghost drawer-button lg:hidden"
19
+ >
20
+ <svg
21
+ width="20"
22
+ height="20"
23
+ xmlns="http://www.w3.org/2000/svg"
24
+ fill="none"
25
+ viewBox="0 0 24 24"
26
+ class="inline-block h-5 w-5 stroke-current md:h-6 md:w-6"
27
+ >
28
+ <path
29
+ stroke-linecap="round"
30
+ stroke-linejoin="round"
31
+ stroke-width="2"
32
+ d="M4 6h16M4 12h16M4 18h16"
33
+ ></path>
34
+ </svg>
35
+ </label>
36
+ </span>
37
+ <div class="flex-1">
38
+ <a
39
+ href="/"
40
+ class={cn('btn btn-ghost text-xl', {
41
+ 'md:hidden': !showBrand,
42
+ })}
43
+ >
44
+ {brand}
45
+ </a>
46
+ </div>
47
+ <div class="hidden flex-none lg:flex">
48
+ <ul class="menu menu-horizontal px-1">
49
+ {Object.entries(navigation).map(([key, entry]) =>
50
+ entry.subEntry ? (
51
+ <li>
52
+ <details>
53
+ <summary>{entry.label ?? key}</summary>
54
+ <ul class="rounded-t-none bg-base-100 p-2">
55
+ {Object.entries(entry.subEntry).map(([key, entry]) => (
56
+ <li>
57
+ <a href={entry.href}>{entry.label ?? key}</a>
58
+ </li>
59
+ ))}
60
+ </ul>
61
+ </details>
62
+ </li>
63
+ ) : (
64
+ <li>
65
+ <a href={entry.href}>{entry.label ?? key}</a>
66
+ </li>
67
+ )
68
+ )}
69
+ </ul>
70
+ </div>
71
+ </div>
@@ -0,0 +1,20 @@
1
+ ---
2
+ import SidebarElement from './SidebarElement.astro';
3
+
4
+ export type Entry = Record<
5
+ string,
6
+ {
7
+ label?: string;
8
+ href?: string;
9
+ subEntry?: Entry;
10
+ }
11
+ >;
12
+
13
+ interface SidebarProps {
14
+ entry: Entry;
15
+ }
16
+
17
+ const { entry } = Astro.props as SidebarProps;
18
+ ---
19
+
20
+ <SidebarElement entry={entry} />
@@ -0,0 +1,26 @@
1
+ ---
2
+ import type { Entry } from './types';
3
+
4
+
5
+ interface Props {
6
+ entry: Entry;
7
+ }
8
+
9
+ const { entry } = Astro.props;
10
+ ---
11
+
12
+ {Object.entries(entry).map(([key, entry]) => {
13
+ const label = entry.label ?? key;
14
+ return (
15
+ <li>
16
+ {entry.href
17
+ ? <a href={entry.href}>{label}</a>
18
+ : <span class='menu-title'>{label}</span>}
19
+ {entry.subEntry ? (
20
+ <ul>
21
+ <Astro.self entry={entry.subEntry} />
22
+ </ul>
23
+ ) : null}
24
+ </li>
25
+ )
26
+ })}
@@ -0,0 +1,36 @@
1
+ ---
2
+ import { cn } from '../tools/cn';
3
+ import SidebarElement from './SidebarElement.astro';
4
+ import type { Entry } from './types';
5
+
6
+ interface Props {
7
+ local?: Entry;
8
+ global: Entry;
9
+ brand: string;
10
+ }
11
+
12
+
13
+ const { local, global, brand } = Astro.props;
14
+ ---
15
+
16
+ <ul class={cn('menu min-h-screen w-56 bg-base-100', { 'md:hidden': !local })}>
17
+ <div>
18
+ <a href="/" class="btn btn-ghost mb-2 text-xl">
19
+ {brand}
20
+ </a>
21
+ </div>
22
+ <div class="block md:hidden">
23
+ <li>
24
+ {local ? (
25
+ <details>
26
+ <summary>Main menu</summary>
27
+ <SidebarElement entry={global} />
28
+ </details>
29
+ ) : (
30
+ <SidebarElement entry={global} />
31
+ )}
32
+ </li>
33
+ <div class={cn('divider my-1 block md:hidden', { hidden: !local })} />
34
+ </div>
35
+ {local && <SidebarElement entry={local} />}
36
+ </ul>
@@ -0,0 +1,23 @@
1
+ ---
2
+ interface Link {
3
+ depth: number;
4
+ text: string;
5
+ slug: string;
6
+ }
7
+
8
+ interface TableOfContentsProps {
9
+ links: Link[];
10
+ }
11
+
12
+ const { links } = Astro.props as TableOfContentsProps;
13
+ ---
14
+
15
+ <div class="fixed right-0 h-full lg:h-auto lg:overflow-y-visible">
16
+ <div class="w-128 sticky" aria-label="Auf dieser Seite">
17
+ {links.map((link, key) => (
18
+ <a href={`#${link.slug}`}>
19
+ {link.text}
20
+ </a>
21
+ ))}
22
+ </div>
23
+ </div>
@@ -0,0 +1,6 @@
1
+ export type Entry = Record<string, {
2
+ label?: string;
3
+ href?: string;
4
+ subEntry?: Entry;
5
+ active?: boolean;
6
+ }>;
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/lib/index.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ import type { AstroIntegration } from 'astro';
2
+ import type { Config } from './schemas/config.ts';
3
+ declare const _default: (config: Config) => AstroIntegration;
4
+ export default _default;
package/lib/index.js ADDED
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const shipyardConfigId = 'virtual:shipyard/config';
4
+ const resolveId = {
5
+ [shipyardConfigId]: `${shipyardConfigId}`,
6
+ };
7
+ const load = (config) => ({
8
+ [shipyardConfigId]: `export default ${JSON.stringify(config)}`,
9
+ });
10
+ exports.default = (config) => ({
11
+ name: 'shipyard',
12
+ hooks: {
13
+ 'astro:config:setup': ({ updateConfig }) => {
14
+ updateConfig({
15
+ vite: {
16
+ plugins: [
17
+ {
18
+ name: 'shipyard',
19
+ resolveId: (id) => resolveId[id],
20
+ load: (id) => load(config)[id],
21
+ },
22
+ ],
23
+ },
24
+ });
25
+ },
26
+ },
27
+ });
@@ -0,0 +1,14 @@
1
+ ---
2
+ import FooterComponent from '@levino/shipyard-base/components/Footer.astro'
3
+
4
+ const locale = Astro.currentLocale
5
+ ---
6
+
7
+ <FooterComponent
8
+ links={[{ href: `/${locale}/imprint`, label: 'Impressum' }]}
9
+ copyright={{
10
+ href: 'https://github.com/levino',
11
+ label: 'Levin Keller',
12
+ year: 2023,
13
+ }}
14
+ />
@@ -0,0 +1,86 @@
1
+ ---
2
+ import Footer from './Footer.astro'
3
+ import '../globals.css'
4
+ import config from 'virtual:shipyard/config'
5
+ import GlobalDesktopNavigation from '../components/GlobalDesktopNavigation.astro'
6
+ import type { NavigationTree, NavigationEntry } from '@/schemas/config'
7
+ import { mapObjIndexed } from 'ramda'
8
+ import SidebarNavigation from '../components/SidebarNavigation.astro'
9
+
10
+ type Props = {
11
+ frontmatter?: {
12
+ title?: string
13
+ description?: string
14
+ sidebarNavigation?: NavigationTree
15
+ }
16
+ } & {
17
+ title?: string
18
+ description?: string
19
+ sidebarNavigation?: NavigationTree
20
+ }
21
+
22
+ const locale = Astro.currentLocale || 'de'
23
+ const currentPath = Astro.url.pathname
24
+ const props = Astro.props.frontmatter || Astro.props
25
+
26
+ const withLocale = (locale: string) => (path: string) => `/${locale}${path}`
27
+ const withCurrentLocale = withLocale(locale)
28
+ const applyLocaleAndSetActive: (navigation: NavigationTree) => NavigationTree =
29
+ mapObjIndexed((entry: NavigationEntry) => ({
30
+ ...entry,
31
+ ...(entry.href ? { href: withCurrentLocale(entry.href) } : {}),
32
+ active: entry.href === currentPath,
33
+ ...(entry.subEntry
34
+ ? { subEntry: applyLocaleAndSetActive(entry.subEntry) }
35
+ : {}),
36
+ }))
37
+
38
+ const navigation = applyLocaleAndSetActive(config.navigation)
39
+ ---
40
+
41
+ <html>
42
+ <head>
43
+ <meta charset="utf-8" />
44
+ <link rel="sitemap" href="/sitemap-index.xml" />
45
+ <title>
46
+ {
47
+ config.meta.title
48
+ ? `Levin Keller - ${config.meta.title}`
49
+ : 'Levin Keller'
50
+ }
51
+ </title>
52
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
53
+ </head>
54
+ <body>
55
+ <div class="drawer lg:drawer-open">
56
+ <input id="drawer" type="checkbox" class="drawer-toggle" />
57
+ <div class="drawer-content">
58
+ <div class="flex min-h-screen flex-col">
59
+ <GlobalDesktopNavigation
60
+ showBrand={!props.sidebarNavigation}
61
+ brand={config.brand}
62
+ navigation={navigation}
63
+ />
64
+ <div class="grow">
65
+ <div class="mx-auto px-4">
66
+ <slot />
67
+ </div>
68
+ </div>
69
+ <Footer />
70
+ </div>
71
+ </div>
72
+
73
+ <div class="drawer-side z-40">
74
+ <label for="drawer" aria-label="close sidebar" class="drawer-overlay"
75
+ ></label>
76
+ <div>
77
+ <SidebarNavigation
78
+ brand={config.brand}
79
+ global={navigation}
80
+ local={props.sidebarNavigation}
81
+ />
82
+ </div>
83
+ </div>
84
+ </div>
85
+ </body>
86
+ </html>
@@ -0,0 +1,7 @@
1
+ ---
2
+ import Base from './Page.astro'
3
+ ---
4
+
5
+ <Base>
6
+ <div class="prose mx-auto"><slot /></div>
7
+ </Base>
@@ -0,0 +1,15 @@
1
+ export interface NavigationEntry {
2
+ label?: string;
3
+ href?: string;
4
+ subEntry?: NavigationTree;
5
+ active?: boolean;
6
+ }
7
+ export type NavigationTree = Record<string, NavigationEntry>;
8
+ export type Config = {
9
+ brand: string;
10
+ navigation: NavigationTree;
11
+ meta: {
12
+ title: string;
13
+ description: string;
14
+ };
15
+ };
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,2 @@
1
+ import { type ClassValue } from 'clsx';
2
+ export declare const cn: (...classes: ClassValue[]) => string;
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.cn = void 0;
7
+ const clsx_1 = __importDefault(require("clsx"));
8
+ const tailwind_merge_1 = require("tailwind-merge");
9
+ const cn = (...classes) => (0, tailwind_merge_1.twMerge)((0, clsx_1.default)(...classes));
10
+ exports.cn = cn;
package/lib/types.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ export interface LinkData {
2
+ href: string;
3
+ label: string;
4
+ active: boolean;
5
+ }
6
+ export declare const MONTHS_EN: readonly ["january", "february", "march", "april", "may", "june", "july", "august", "september", "october", "november", "december"];
7
+ export declare const MONTHS_DE: readonly ["Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember"];
package/lib/types.js ADDED
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MONTHS_DE = exports.MONTHS_EN = void 0;
4
+ exports.MONTHS_EN = [
5
+ 'january',
6
+ 'february',
7
+ 'march',
8
+ 'april',
9
+ 'may',
10
+ 'june',
11
+ 'july',
12
+ 'august',
13
+ 'september',
14
+ 'october',
15
+ 'november',
16
+ 'december',
17
+ ];
18
+ exports.MONTHS_DE = [
19
+ 'Januar',
20
+ 'Februar',
21
+ 'März',
22
+ 'April',
23
+ 'Mai',
24
+ 'Juni',
25
+ 'Juli',
26
+ 'August',
27
+ 'September',
28
+ 'Oktober',
29
+ 'November',
30
+ 'Dezember',
31
+ ];
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "@levino/shipyard-base",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "main": "lib/index.js",
6
+ "peerDependencies": {
7
+ "astro": "^5",
8
+ "daisyui": "^4",
9
+ "tailwindcss": "^3",
10
+ "@tailwindcss/typography": "^0.5.10"
11
+ },
12
+ "scripts": {
13
+ "build": "tsc -p tsconfig.build.json && rsync -avm --include='*/' --include='*.astro' --exclude='*' ./src/ ./lib/"
14
+ },
15
+ "files": [
16
+ "lib"
17
+ ],
18
+ "devDependencies": {
19
+ "@tailwindcss/typography": "^0.5.10",
20
+ "@types/ramda": "^0.29.9",
21
+ "daisyui": "^4.6.0",
22
+ "tailwindcss": "^3.4.0",
23
+ "typescript": "^5.2.2",
24
+ "vite": "^4"
25
+ },
26
+ "dependencies": {
27
+ "clsx": "^2.1.0",
28
+ "ramda": "^0.29.1",
29
+ "tailwind-merge": "^2.2.0"
30
+ }
31
+ }