@uniweb/kit 0.1.10 → 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 +355 -155
- package/package.json +2 -2
- package/src/components/Link/Link.jsx +4 -2
- package/src/hooks/index.js +11 -0
- package/src/hooks/useInView.js +200 -0
- package/src/hooks/useThemeData.js +256 -0
- package/src/hooks/useVersion.js +128 -0
- package/src/index.js +15 -2
- package/src/search/hooks.js +84 -0
- package/src/search/index.js +1 -1
- package/src/styled/Asset/Asset.jsx +2 -2
- package/src/utils/index.js +69 -0
package/src/search/hooks.js
CHANGED
|
@@ -236,4 +236,88 @@ export function useSearchIndex(website, options = {}) {
|
|
|
236
236
|
}
|
|
237
237
|
}
|
|
238
238
|
|
|
239
|
+
/**
|
|
240
|
+
* Hook for Cmd/Ctrl+K keyboard shortcut to open search
|
|
241
|
+
*
|
|
242
|
+
* @param {Function|Object} callbacks - Either onOpen function, or { onOpen, onPreload }
|
|
243
|
+
*
|
|
244
|
+
* @example
|
|
245
|
+
* // Simple usage
|
|
246
|
+
* useSearchShortcut(() => setSearchOpen(true))
|
|
247
|
+
*
|
|
248
|
+
* // With preload
|
|
249
|
+
* useSearchShortcut({
|
|
250
|
+
* onOpen: () => setSearchOpen(true),
|
|
251
|
+
* onPreload: () => search.preload()
|
|
252
|
+
* })
|
|
253
|
+
*/
|
|
254
|
+
export function useSearchShortcut(callbacks) {
|
|
255
|
+
const { onOpen, onPreload } = typeof callbacks === 'function'
|
|
256
|
+
? { onOpen: callbacks, onPreload: null }
|
|
257
|
+
: callbacks
|
|
258
|
+
|
|
259
|
+
useEffect(() => {
|
|
260
|
+
const handleKeyDown = (e) => {
|
|
261
|
+
if ((e.metaKey || e.ctrlKey) && e.key === 'k') {
|
|
262
|
+
e.preventDefault()
|
|
263
|
+
onPreload?.()
|
|
264
|
+
onOpen()
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
window.addEventListener('keydown', handleKeyDown)
|
|
269
|
+
return () => window.removeEventListener('keydown', handleKeyDown)
|
|
270
|
+
}, [onOpen, onPreload])
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Hook that wraps useSearch with intent-based preloading
|
|
275
|
+
*
|
|
276
|
+
* Provides handlers to trigger preload on user intent (hover, focus, touch)
|
|
277
|
+
* rather than on component mount. This saves bandwidth for users who never search.
|
|
278
|
+
*
|
|
279
|
+
* @param {Object} website - Website instance from @uniweb/core
|
|
280
|
+
* @param {Object} options - Options passed to useSearch
|
|
281
|
+
* @returns {Object} Search state, methods, and intent handlers
|
|
282
|
+
*
|
|
283
|
+
* @example
|
|
284
|
+
* function SearchButton({ onClick }) {
|
|
285
|
+
* const { intentProps, triggerPreload } = useSearchWithIntent(website)
|
|
286
|
+
*
|
|
287
|
+
* useSearchShortcut({
|
|
288
|
+
* onOpen: onClick,
|
|
289
|
+
* onPreload: triggerPreload,
|
|
290
|
+
* })
|
|
291
|
+
*
|
|
292
|
+
* return (
|
|
293
|
+
* <button onClick={onClick} {...intentProps}>
|
|
294
|
+
* Search
|
|
295
|
+
* </button>
|
|
296
|
+
* )
|
|
297
|
+
* }
|
|
298
|
+
*/
|
|
299
|
+
export function useSearchWithIntent(website, options = {}) {
|
|
300
|
+
const search = useSearch(website, options)
|
|
301
|
+
const hasPreloaded = useRef(false)
|
|
302
|
+
|
|
303
|
+
const triggerPreload = useCallback(() => {
|
|
304
|
+
if (hasPreloaded.current) return
|
|
305
|
+
hasPreloaded.current = true
|
|
306
|
+
search.preload()
|
|
307
|
+
}, [search])
|
|
308
|
+
|
|
309
|
+
// Intent handlers - spread onto interactive elements
|
|
310
|
+
const intentProps = useMemo(() => ({
|
|
311
|
+
onMouseEnter: triggerPreload,
|
|
312
|
+
onFocus: triggerPreload,
|
|
313
|
+
onTouchStart: triggerPreload,
|
|
314
|
+
}), [triggerPreload])
|
|
315
|
+
|
|
316
|
+
return {
|
|
317
|
+
...search,
|
|
318
|
+
triggerPreload,
|
|
319
|
+
intentProps,
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
239
323
|
export default useSearch
|
package/src/search/index.js
CHANGED
|
@@ -23,4 +23,4 @@
|
|
|
23
23
|
|
|
24
24
|
export { createSearchClient, loadSearchIndex, clearSearchCache } from './client.js'
|
|
25
25
|
export { buildSnippet, highlightMatches, escapeHtml } from './snippets.js'
|
|
26
|
-
export { useSearch, useSearchIndex } from './hooks.js'
|
|
26
|
+
export { useSearch, useSearchIndex, useSearchShortcut, useSearchWithIntent } from './hooks.js'
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
|
|
9
9
|
import React, { useState, useCallback, forwardRef, useImperativeHandle } from 'react'
|
|
10
10
|
import { cn } from '../../utils/index.js'
|
|
11
|
-
import { FileLogo } from '
|
|
12
|
-
import { Image } from '
|
|
11
|
+
import { FileLogo } from '../../components/FileLogo/index.js'
|
|
12
|
+
import { Image } from '../../components/Image/index.js'
|
|
13
13
|
import { useWebsite } from '../../hooks/useWebsite.js'
|
|
14
14
|
|
|
15
15
|
/**
|
package/src/utils/index.js
CHANGED
|
@@ -9,6 +9,75 @@ import { twMerge, twJoin } from 'tailwind-merge'
|
|
|
9
9
|
// Re-export tailwind-merge utilities
|
|
10
10
|
export { twMerge, twJoin }
|
|
11
11
|
|
|
12
|
+
// ─────────────────────────────────────────────────────────────────
|
|
13
|
+
// Locale Utilities
|
|
14
|
+
// ─────────────────────────────────────────────────────────────────
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Common locale display names (native language names)
|
|
18
|
+
* Used as fallback when site.yml doesn't specify labels.
|
|
19
|
+
* Tree-shakeable: only included if foundation uses getLocaleLabel().
|
|
20
|
+
*/
|
|
21
|
+
export const LOCALE_DISPLAY_NAMES = {
|
|
22
|
+
en: 'English',
|
|
23
|
+
es: 'Español',
|
|
24
|
+
fr: 'Français',
|
|
25
|
+
de: 'Deutsch',
|
|
26
|
+
it: 'Italiano',
|
|
27
|
+
pt: 'Português',
|
|
28
|
+
nl: 'Nederlands',
|
|
29
|
+
pl: 'Polski',
|
|
30
|
+
ru: 'Русский',
|
|
31
|
+
ja: '日本語',
|
|
32
|
+
ko: '한국어',
|
|
33
|
+
zh: '中文',
|
|
34
|
+
'zh-CN': '简体中文',
|
|
35
|
+
'zh-TW': '繁體中文',
|
|
36
|
+
ar: 'العربية',
|
|
37
|
+
he: 'עברית',
|
|
38
|
+
hi: 'हिन्दी',
|
|
39
|
+
th: 'ไทย',
|
|
40
|
+
vi: 'Tiếng Việt',
|
|
41
|
+
tr: 'Türkçe',
|
|
42
|
+
uk: 'Українська',
|
|
43
|
+
cs: 'Čeština',
|
|
44
|
+
el: 'Ελληνικά',
|
|
45
|
+
hu: 'Magyar',
|
|
46
|
+
ro: 'Română',
|
|
47
|
+
sv: 'Svenska',
|
|
48
|
+
da: 'Dansk',
|
|
49
|
+
fi: 'Suomi',
|
|
50
|
+
no: 'Norsk',
|
|
51
|
+
id: 'Bahasa Indonesia',
|
|
52
|
+
ms: 'Bahasa Melayu'
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Get display label for a locale
|
|
57
|
+
* Priority: locale.label (from site config) → LOCALE_DISPLAY_NAMES → code.toUpperCase()
|
|
58
|
+
*
|
|
59
|
+
* @param {Object|string} locale - Locale object {code, label?} or locale code string
|
|
60
|
+
* @returns {string} Display label for the locale
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* getLocaleLabel({ code: 'es', label: 'Spanish' }) // 'Spanish'
|
|
64
|
+
* getLocaleLabel({ code: 'es' }) // 'Español'
|
|
65
|
+
* getLocaleLabel('es') // 'Español'
|
|
66
|
+
* getLocaleLabel({ code: 'xx' }) // 'XX'
|
|
67
|
+
*/
|
|
68
|
+
export function getLocaleLabel(locale) {
|
|
69
|
+
// Handle string input (just a code)
|
|
70
|
+
if (typeof locale === 'string') {
|
|
71
|
+
return LOCALE_DISPLAY_NAMES[locale] || locale.toUpperCase()
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Handle object input
|
|
75
|
+
if (!locale || !locale.code) return ''
|
|
76
|
+
|
|
77
|
+
// Priority: explicit label → known display name → uppercase code
|
|
78
|
+
return locale.label || LOCALE_DISPLAY_NAMES[locale.code] || locale.code.toUpperCase()
|
|
79
|
+
}
|
|
80
|
+
|
|
12
81
|
/**
|
|
13
82
|
* Merge class names with Tailwind CSS conflict resolution
|
|
14
83
|
* @param {...string} classes - Class names to merge
|