@zenithbuild/core 0.6.2 → 1.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.
@@ -1,231 +0,0 @@
1
- <script>
2
- // Props extend HTMLAnchorElement properties + custom ZenLink attributes
3
- // Standard anchor attributes: href, target, rel, download, hreflang, type, ping, referrerPolicy, etc.
4
- // Custom ZenLink attributes: preload, exact, onClick
5
- type Props = {
6
- // Standard HTMLAnchorElement attributes
7
- href?: string
8
- target?: '_blank' | '_self' | '_parent' | '_top' | string
9
- rel?: string
10
- download?: string | boolean
11
- hreflang?: string
12
- type?: string
13
- ping?: string
14
- referrerPolicy?: string
15
- class?: string
16
- id?: string
17
- title?: string
18
- ariaLabel?: string
19
- role?: string
20
- tabIndex?: number | string
21
- // Custom ZenLink attributes
22
- preload?: boolean
23
- exact?: boolean
24
- onClick?: (event?: MouseEvent) => void | boolean
25
- }
26
-
27
- /**
28
- * Handle link click - prevents default and uses SPA navigation
29
- * Respects target="_blank" and other standard anchor behaviors
30
- */
31
- function handleClick(event, el) {
32
- // Ensure attributes are set from props
33
- if (el) {
34
- ensureAttributes(el)
35
- }
36
-
37
- // Get target from the element attribute (more reliable than prop)
38
- const linkTarget = el ? el.getAttribute('target') : (typeof target !== 'undefined' ? target : null)
39
-
40
- // If target is _blank, _parent, or _top, let browser handle it (opens in new tab/window)
41
- if (linkTarget === '_blank' || linkTarget === '_parent' || linkTarget === '_top') {
42
- // Let browser handle standard navigation
43
- return
44
- }
45
-
46
- // Allow modifier keys for native behavior (Cmd/Ctrl+click, etc.)
47
- if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) {
48
- return
49
- }
50
-
51
- // Get href from element or prop
52
- const linkHref = el ? el.getAttribute('href') : (typeof href !== 'undefined' ? href : null)
53
- if (!linkHref) return
54
-
55
- // Check if external URL (http://, https://, //, mailto:, tel:, etc.)
56
- if (linkHref.startsWith('http://') ||
57
- linkHref.startsWith('https://') ||
58
- linkHref.startsWith('//') ||
59
- linkHref.startsWith('mailto:') ||
60
- linkHref.startsWith('tel:') ||
61
- linkHref.startsWith('javascript:')) {
62
- // External/special link - open in new tab if target not specified
63
- if (!linkTarget) {
64
- el?.setAttribute('target', '_blank')
65
- el?.setAttribute('rel', 'noopener noreferrer')
66
- }
67
- // Let browser handle it
68
- return
69
- }
70
-
71
- // Prevent default navigation for internal SPA links
72
- event.preventDefault()
73
- event.stopPropagation()
74
-
75
- // Call onClick prop if provided
76
- if (typeof onClick === 'function') {
77
- const result = onClick(event)
78
- // If onClick returns false, cancel navigation
79
- if (result === false) {
80
- return
81
- }
82
- }
83
-
84
- // Normalize path for comparison
85
- const normalizedHref = linkHref === '' ? '/' : linkHref
86
- const currentPath = window.location.pathname === '' ? '/' : window.location.pathname
87
-
88
- // Only navigate if path is different (idempotent navigation)
89
- if (normalizedHref !== currentPath) {
90
- console.log('[ZenLink] Navigating to:', linkHref)
91
- // Navigate using SPA router
92
- if (window.__zenith_router && window.__zenith_router.navigate) {
93
- console.log('[ZenLink] Using router.navigate')
94
- window.__zenith_router.navigate(linkHref)
95
- } else {
96
- console.log('[ZenLink] Using fallback history API')
97
- // Fallback to history API
98
- window.history.pushState(null, '', linkHref)
99
- window.dispatchEvent(new PopStateEvent('popstate'))
100
- }
101
- } else {
102
- console.log('[ZenLink] Already on route:', linkHref, '- skipping navigation')
103
- }
104
- }
105
-
106
- /**
107
- * Handle mouse enter for preloading
108
- */
109
- function handleMouseEnter(event, el) {
110
- // Ensure attributes are set
111
- if (el) {
112
- ensureAttributes(el)
113
- }
114
-
115
- const shouldPreload = typeof preload !== 'undefined' ? preload : false
116
- console.log('[ZenLink] handleMouseEnter called, preload:', shouldPreload)
117
- if (!shouldPreload) {
118
- console.log('[ZenLink] Preload disabled, returning early')
119
- return
120
- }
121
-
122
- const linkHref = el ? el.getAttribute('href') : (typeof href !== 'undefined' ? href : null)
123
- if (!linkHref) {
124
- return
125
- }
126
-
127
- // Skip external URLs
128
- if (linkHref.startsWith('http://') || linkHref.startsWith('https://') || linkHref.startsWith('//')) {
129
- return
130
- }
131
-
132
- console.log('[ZenLink] Prefetch triggered on hover:', linkHref)
133
-
134
- // Prefetch the route
135
- if (window.__zenith_router && window.__zenith_router.prefetch) {
136
- console.log('[ZenLink] Calling router.prefetch for:', linkHref)
137
- window.__zenith_router.prefetch(linkHref).then(() => {
138
- console.log('[ZenLink] Prefetch complete for:', linkHref)
139
- }).catch((error) => {
140
- console.warn('[ZenLink] Prefetch failed for:', linkHref, error)
141
- })
142
- } else {
143
- console.warn('[ZenLink] Router prefetch not available')
144
- }
145
- }
146
-
147
- // Apply attributes on mount
148
- if (typeof zenOnMount !== 'undefined') {
149
- zenOnMount(() => {
150
- setTimeout(() => {
151
- // Find all ZenLink anchor elements and apply attributes
152
- document.querySelectorAll('a[data-zen-component="Zenlink"]').forEach(el => {
153
- ensureAttributes(el)
154
- })
155
- }, 0)
156
- })
157
- }
158
-
159
- /**
160
- * Apply standard anchor attributes from props to the element
161
- * Called when the element is clicked to ensure attributes are set
162
- */
163
- function ensureAttributes(el) {
164
- if (!el) return
165
-
166
- // Set attributes from props (only if they exist and aren't already set)
167
- const attrs = {
168
- target: typeof target !== 'undefined' ? target : null,
169
- rel: typeof rel !== 'undefined' ? rel : null,
170
- download: typeof download !== 'undefined' ? download : null,
171
- hreflang: typeof hreflang !== 'undefined' ? hreflang : null,
172
- type: typeof type !== 'undefined' ? type : null,
173
- ping: typeof ping !== 'undefined' ? ping : null,
174
- referrerPolicy: typeof referrerPolicy !== 'undefined' ? referrerPolicy : null,
175
- id: typeof id !== 'undefined' ? id : null,
176
- title: typeof title !== 'undefined' ? title : null,
177
- ariaLabel: typeof ariaLabel !== 'undefined' ? ariaLabel : null,
178
- role: typeof role !== 'undefined' ? role : null,
179
- tabIndex: typeof tabIndex !== 'undefined' ? tabIndex : null
180
- }
181
-
182
- // Map to HTML attribute names
183
- const htmlAttrs = {
184
- target: 'target',
185
- rel: 'rel',
186
- download: 'download',
187
- hreflang: 'hreflang',
188
- type: 'type',
189
- ping: 'ping',
190
- referrerPolicy: 'referrerpolicy',
191
- id: 'id',
192
- title: 'title',
193
- ariaLabel: 'aria-label',
194
- role: 'role',
195
- tabIndex: 'tabindex'
196
- }
197
-
198
- // Set attributes that have values
199
- for (const [prop, value] of Object.entries(attrs)) {
200
- if (value !== null && value !== undefined && value !== '') {
201
- const htmlAttr = htmlAttrs[prop]
202
- if (htmlAttr && !el.hasAttribute(htmlAttr)) {
203
- el.setAttribute(htmlAttr, String(value))
204
- }
205
- }
206
- }
207
- }
208
-
209
- </script>
210
-
211
- <style>
212
- .zen-link {
213
- color: inherit;
214
- text-decoration: none;
215
- cursor: pointer;
216
- }
217
-
218
- .zen-link:hover {
219
- text-decoration: underline;
220
- }
221
- </style>
222
-
223
- <a
224
- href="{ href }"
225
- class="zen-link { class }"
226
- onclick="handleClick"
227
- onmouseenter="handleMouseEnter"
228
- style="cursor: pointer;"
229
- >
230
- <slot />
231
- </a>
@@ -1,78 +0,0 @@
1
- /**
2
- * Zenith Navigation System
3
- *
4
- * Provides SPA navigation utilities and the ZenLink API.
5
- *
6
- * @example
7
- * ```ts
8
- * import { navigate, isActive, prefetch, zenLink } from 'zenith/core'
9
- *
10
- * // Programmatic navigation
11
- * navigate('/about')
12
- *
13
- * // Check active state
14
- * if (isActive('/blog')) {
15
- * console.log('On blog section')
16
- * }
17
- *
18
- * // Prefetch for faster navigation
19
- * prefetch('/dashboard')
20
- *
21
- * // Create link programmatically
22
- * const link = zenLink({ href: '/contact', children: 'Contact' })
23
- * ```
24
- */
25
-
26
- // Export all navigation utilities
27
- export {
28
- // Navigation API
29
- zenNavigate,
30
- navigate,
31
- zenBack,
32
- back,
33
- zenForward,
34
- forward,
35
- zenGo,
36
- go,
37
-
38
- // Active state
39
- zenIsActive,
40
- isActive,
41
-
42
- // Prefetching
43
- zenPrefetch,
44
- prefetch,
45
- zenIsPrefetched,
46
- isPrefetched,
47
-
48
- // Transitions API
49
- setGlobalTransition,
50
- getGlobalTransition,
51
- createTransitionContext,
52
-
53
- // Route state
54
- zenGetRoute,
55
- getRoute,
56
- zenGetParam,
57
- getParam,
58
- zenGetQuery,
59
- getQuery,
60
-
61
- // ZenLink factory
62
- createZenLink,
63
- zenLink,
64
-
65
- // Utilities
66
- isExternalUrl,
67
- shouldUseSPANavigation,
68
- normalizePath
69
- } from './zen-link'
70
-
71
- // Export types
72
- export type {
73
- ZenLinkProps,
74
- TransitionContext,
75
- TransitionHandler,
76
- NavigateOptions
77
- } from './zen-link'
78
-