@levino/shipyard-base 0.5.7 → 0.5.8-rc-20251122115645

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,63 @@
1
+ ---
2
+ import type { Entry } from './types'
3
+
4
+ interface BreadcrumbItem {
5
+ label: string
6
+ href?: string
7
+ }
8
+
9
+ interface Props {
10
+ navigation: Entry
11
+ currentPath?: string
12
+ }
13
+
14
+ const { navigation, currentPath = Astro.url.pathname } = Astro.props
15
+
16
+ // Normalize path by removing trailing slash for comparison
17
+ const normalizePath = (path: string) => path.replace(/\/$/, '') || '/'
18
+
19
+ // Find the path to the current page in the navigation tree
20
+ const findBreadcrumbs = (
21
+ entry: Entry,
22
+ targetPath: string,
23
+ path: BreadcrumbItem[] = [],
24
+ ): BreadcrumbItem[] | null => {
25
+ for (const [key, item] of Object.entries(entry)) {
26
+ const label = item.label ?? key
27
+ const currentItem: BreadcrumbItem = { label, href: item.href }
28
+
29
+ if (item.href && normalizePath(item.href) === normalizePath(targetPath)) {
30
+ return [...path, currentItem]
31
+ }
32
+
33
+ if (item.subEntry) {
34
+ const result = findBreadcrumbs(item.subEntry, targetPath, [
35
+ ...path,
36
+ currentItem,
37
+ ])
38
+ if (result) return result
39
+ }
40
+ }
41
+ return null
42
+ }
43
+
44
+ const breadcrumbs = findBreadcrumbs(navigation, currentPath) ?? []
45
+ ---
46
+
47
+ {
48
+ breadcrumbs.length > 0 && (
49
+ <div class="breadcrumbs text-sm">
50
+ <ul>
51
+ {breadcrumbs.map((item, index) => (
52
+ <li>
53
+ {item.href && index < breadcrumbs.length - 1 ? (
54
+ <a href={item.href}>{item.label}</a>
55
+ ) : (
56
+ <span>{item.label}</span>
57
+ )}
58
+ </li>
59
+ ))}
60
+ </ul>
61
+ </div>
62
+ )
63
+ }
@@ -1,23 +1,43 @@
1
1
  ---
2
+ import { cn } from '../../src/tools/cn'
2
3
  import type { Entry } from './types'
3
4
 
4
5
  interface Props {
5
6
  entry: Entry
7
+ currentPath?: string
6
8
  }
7
9
 
8
- const { entry } = Astro.props
10
+ const { entry, currentPath = Astro.url.pathname } = Astro.props
11
+
12
+ // Normalize path by removing trailing slash for comparison
13
+ const normalizePath = (path: string) => path.replace(/\/$/, '') || '/'
14
+
15
+ // Check if entry or any of its children are active
16
+ const isActiveOrHasActiveChild = (entryValue: Entry[string]): boolean => {
17
+ if (
18
+ entryValue.href &&
19
+ normalizePath(entryValue.href) === normalizePath(currentPath)
20
+ )
21
+ return true
22
+ if (entryValue.subEntry) {
23
+ return Object.values(entryValue.subEntry).some(isActiveOrHasActiveChild)
24
+ }
25
+ return false
26
+ }
9
27
  ---
10
28
 
11
- {Object.entries(entry).map(([key, entry]) => {
12
- const label = entry.label ?? key;
29
+ {Object.entries(entry).map(([key, entryValue]) => {
30
+ const label = entryValue.label ?? key;
31
+ const isActive = entryValue.href && normalizePath(entryValue.href) === normalizePath(currentPath);
32
+ const hasActiveChild = entryValue.subEntry && Object.values(entryValue.subEntry).some(isActiveOrHasActiveChild);
13
33
  return (
14
- <li>
15
- {entry.href
16
- ? <a href={entry.href}>{label}</a>
34
+ <li class={entryValue.className}>
35
+ {entryValue.href
36
+ ? <a href={entryValue.href} class={cn({ 'bg-base-200/50 font-medium text-primary': isActive })}>{label}</a>
17
37
  : <span class='menu-title'>{label}</span>}
18
- {entry.subEntry ? (
38
+ {entryValue.subEntry ? (
19
39
  <ul>
20
- <Astro.self entry={entry.subEntry} />
40
+ <Astro.self entry={entryValue.subEntry} currentPath={currentPath} />
21
41
  </ul>
22
42
  ) : null}
23
43
  </li>
@@ -1,19 +1,84 @@
1
1
  ---
2
+ import { cn } from '../../src/tools/cn'
3
+
2
4
  interface Link {
3
5
  depth: number
4
6
  text: string
5
7
  slug: string
6
8
  }
7
9
 
8
- interface TableOfContentsProps {
10
+ interface Props {
9
11
  links: Link[]
12
+ label?: string
13
+ class?: string
14
+ desktopOnly?: boolean
10
15
  }
11
16
 
12
- const { links } = Astro.props as TableOfContentsProps
17
+ const {
18
+ links,
19
+ label = 'On this page',
20
+ class: className,
21
+ desktopOnly = false,
22
+ } = Astro.props
23
+
24
+ // Filter to only include h2 and h3 headings (depth 2 and 3)
25
+ const filteredLinks = links.filter((link) => link.depth >= 2 && link.depth <= 3)
13
26
  ---
14
27
 
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) => <a href={`#${link.slug}`}>{link.text}</a>)}
18
- </div>
19
- </div>
28
+ {
29
+ filteredLinks.length > 0 && (
30
+ <div class={className}>
31
+ {/* Mobile: Collapsible dropdown */}
32
+ {!desktopOnly && (
33
+ <div class="xl:hidden mb-4">
34
+ <details class="collapse collapse-arrow bg-base-200">
35
+ <summary class="collapse-title min-h-0 py-2 font-medium">
36
+ {label}
37
+ </summary>
38
+ <div class="collapse-content">
39
+ <ul class="menu menu-sm">
40
+ {filteredLinks.map((link) => (
41
+ <li>
42
+ <a
43
+ href={`#${link.slug}`}
44
+ class={cn({
45
+ 'pl-4': link.depth === 2,
46
+ 'pl-8': link.depth === 3,
47
+ })}
48
+ >
49
+ {link.text}
50
+ </a>
51
+ </li>
52
+ ))}
53
+ </ul>
54
+ </div>
55
+ </details>
56
+ </div>
57
+ )}
58
+
59
+ {/* Desktop: Sidebar within the grid column */}
60
+ {desktopOnly && (
61
+ <div class="border-l border-base-300 pl-4">
62
+ <h4 class="font-medium text-sm mb-2 text-base-content/60">
63
+ {label}
64
+ </h4>
65
+ <ul class="menu menu-xs p-0">
66
+ {filteredLinks.map((link) => (
67
+ <li>
68
+ <a
69
+ href={`#${link.slug}`}
70
+ class={cn('text-base-content/70 hover:text-primary', {
71
+ 'pl-0': link.depth === 2,
72
+ 'pl-4': link.depth === 3,
73
+ })}
74
+ >
75
+ {link.text}
76
+ </a>
77
+ </li>
78
+ ))}
79
+ </ul>
80
+ </div>
81
+ )}
82
+ </div>
83
+ )
84
+ }
@@ -1,3 +1,4 @@
1
+ export { default as Breadcrumbs } from './Breadcrumbs.astro'
1
2
  export { default as Footer } from './Footer.astro'
2
3
  export { default as GlobalDesktopNavigation } from './GlobalDesktopNavigation.astro'
3
4
  export { default as LocalNavigation } from './LocalNavigation.astro'
@@ -5,5 +5,6 @@ export type Entry = Record<
5
5
  href?: string
6
6
  subEntry?: Entry
7
7
  active?: boolean
8
+ className?: string
8
9
  }
9
10
  >
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@levino/shipyard-base",
3
- "version": "0.5.7",
3
+ "version": "0.5.8-rc-20251122115645",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "exports": {