@uniweb/kit 0.1.8 → 0.1.10
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/package.json +2 -2
- package/src/hooks/useActiveRoute.js +26 -34
- package/src/hooks/useWebsite.js +10 -12
- package/src/styled/Article/index.jsx +79 -0
- package/src/styled/Prose/index.jsx +66 -0
- package/src/styled/index.js +7 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uniweb/kit",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.10",
|
|
4
4
|
"description": "Standard component library for Uniweb foundations",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"tailwind-merge": "^2.6.0",
|
|
40
|
-
"@uniweb/core": "0.1.
|
|
40
|
+
"@uniweb/core": "0.1.15"
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|
|
43
43
|
"react": "^18.0.0 || ^19.0.0",
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* useActiveRoute Hook
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Hook for active route detection in navigation components.
|
|
5
|
+
* All route comparison logic is delegated to Website class methods
|
|
6
|
+
* to ensure a single source of truth for route normalization and matching.
|
|
6
7
|
*
|
|
7
8
|
* @example
|
|
8
9
|
* function NavItem({ page }) {
|
|
@@ -13,25 +14,18 @@
|
|
|
13
14
|
* href={page.getNavigableRoute()}
|
|
14
15
|
* className={isActiveOrAncestor(page) ? 'active' : ''}
|
|
15
16
|
* >
|
|
16
|
-
* {page.
|
|
17
|
+
* {page.getLabel()}
|
|
17
18
|
* </Link>
|
|
18
19
|
* )
|
|
19
20
|
* }
|
|
20
21
|
*/
|
|
21
22
|
|
|
22
23
|
import { useRouting } from './useRouting.js'
|
|
24
|
+
import { useWebsite } from './useWebsite.js'
|
|
23
25
|
|
|
24
26
|
/**
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
* @returns {string}
|
|
28
|
-
*/
|
|
29
|
-
function normalizeRoute(route) {
|
|
30
|
-
return (route || '').replace(/^\//, '').replace(/\/$/, '')
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Hook for active route detection with SSG-safe fallbacks.
|
|
27
|
+
* Hook for active route detection.
|
|
28
|
+
* Delegates all logic to Website class for consistency.
|
|
35
29
|
*
|
|
36
30
|
* @returns {Object} Route utilities
|
|
37
31
|
* @property {string} route - Current normalized route (e.g., 'docs/getting-started')
|
|
@@ -41,17 +35,18 @@ function normalizeRoute(route) {
|
|
|
41
35
|
*/
|
|
42
36
|
export function useActiveRoute() {
|
|
43
37
|
const { useLocation } = useRouting()
|
|
38
|
+
const { website } = useWebsite()
|
|
44
39
|
const location = useLocation()
|
|
45
40
|
|
|
46
|
-
const
|
|
47
|
-
const rootSegment =
|
|
41
|
+
const currentRoute = website.normalizeRoute(location?.pathname)
|
|
42
|
+
const rootSegment = currentRoute.split('/')[0]
|
|
48
43
|
|
|
49
44
|
return {
|
|
50
45
|
/**
|
|
51
46
|
* Current normalized route (no leading/trailing slashes)
|
|
52
47
|
* @type {string}
|
|
53
48
|
*/
|
|
54
|
-
route,
|
|
49
|
+
route: currentRoute,
|
|
55
50
|
|
|
56
51
|
/**
|
|
57
52
|
* First segment of the current route
|
|
@@ -62,34 +57,31 @@ export function useActiveRoute() {
|
|
|
62
57
|
|
|
63
58
|
/**
|
|
64
59
|
* Check if a page is the current active page (exact match)
|
|
60
|
+
* Delegates to Website.isRouteActive() for consistent comparison.
|
|
65
61
|
*
|
|
66
|
-
* @param {Object}
|
|
62
|
+
* @param {Object|string} pageOrRoute - Page object or route string
|
|
67
63
|
* @returns {boolean}
|
|
68
64
|
*/
|
|
69
|
-
isActive: (
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
return normalizeRoute(page.route) === route
|
|
65
|
+
isActive: (pageOrRoute) => {
|
|
66
|
+
const targetRoute = typeof pageOrRoute === 'string'
|
|
67
|
+
? pageOrRoute
|
|
68
|
+
: pageOrRoute.route
|
|
69
|
+
return website.isRouteActive(targetRoute, currentRoute)
|
|
75
70
|
},
|
|
76
71
|
|
|
77
72
|
/**
|
|
78
73
|
* Check if a page or any of its descendants is active
|
|
79
|
-
* Useful for highlighting parent nav items when child is active
|
|
74
|
+
* Useful for highlighting parent nav items when child is active.
|
|
75
|
+
* Delegates to Website.isRouteActiveOrAncestor() for consistent logic.
|
|
80
76
|
*
|
|
81
|
-
* @param {Object}
|
|
77
|
+
* @param {Object|string} pageOrRoute - Page object or route string
|
|
82
78
|
* @returns {boolean}
|
|
83
79
|
*/
|
|
84
|
-
isActiveOrAncestor: (
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
const pageRoute = normalizeRoute(page.route)
|
|
90
|
-
if (pageRoute === route) return true
|
|
91
|
-
if (pageRoute === '') return true // Root is ancestor of all
|
|
92
|
-
return route.startsWith(pageRoute + '/')
|
|
80
|
+
isActiveOrAncestor: (pageOrRoute) => {
|
|
81
|
+
const targetRoute = typeof pageOrRoute === 'string'
|
|
82
|
+
? pageOrRoute
|
|
83
|
+
: pageOrRoute.route
|
|
84
|
+
return website.isRouteActiveOrAncestor(targetRoute, currentRoute)
|
|
93
85
|
},
|
|
94
86
|
}
|
|
95
87
|
}
|
package/src/hooks/useWebsite.js
CHANGED
|
@@ -14,25 +14,23 @@
|
|
|
14
14
|
import { getUniweb } from '@uniweb/core'
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
|
-
* Get the current website instance and utilities
|
|
17
|
+
* Get the current website instance and utilities.
|
|
18
|
+
* This hook assumes the runtime is properly initialized.
|
|
19
|
+
*
|
|
18
20
|
* @returns {Object} Website utilities
|
|
21
|
+
* @throws {Error} If called before runtime initialization
|
|
19
22
|
*/
|
|
20
23
|
export function useWebsite() {
|
|
21
24
|
const uniweb = getUniweb()
|
|
25
|
+
const website = uniweb?.activeWebsite
|
|
22
26
|
|
|
23
|
-
if (!
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
makeHref: (href) => href,
|
|
29
|
-
getLanguage: () => 'en',
|
|
30
|
-
getLanguages: () => []
|
|
31
|
-
}
|
|
27
|
+
if (!website) {
|
|
28
|
+
throw new Error(
|
|
29
|
+
'[Kit] useWebsite() called before runtime initialization. ' +
|
|
30
|
+
'Components must be rendered within a properly initialized Uniweb runtime.'
|
|
31
|
+
)
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
const website = uniweb.activeWebsite
|
|
35
|
-
|
|
36
34
|
return {
|
|
37
35
|
/**
|
|
38
36
|
* The active Website instance
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Article Component
|
|
3
|
+
*
|
|
4
|
+
* Semantic article wrapper with prose typography.
|
|
5
|
+
* Use for blog posts, news articles, documentation pages, etc.
|
|
6
|
+
*
|
|
7
|
+
* @module @uniweb/kit/styled/Article
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import React from 'react'
|
|
11
|
+
import { cn } from '../../utils/index.js'
|
|
12
|
+
import { Render } from '../Section/Render.jsx'
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Prose sizes
|
|
16
|
+
*/
|
|
17
|
+
const SIZE_CLASSES = {
|
|
18
|
+
sm: 'prose-sm',
|
|
19
|
+
base: 'prose-base',
|
|
20
|
+
lg: 'prose-lg',
|
|
21
|
+
xl: 'prose-xl',
|
|
22
|
+
'2xl': 'prose-2xl'
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Article - Semantic article with prose typography
|
|
27
|
+
*
|
|
28
|
+
* Renders content inside a semantic <article> tag with prose styling.
|
|
29
|
+
* Can accept either children or a content prop (ProseMirror JSON).
|
|
30
|
+
*
|
|
31
|
+
* @param {Object} props
|
|
32
|
+
* @param {Object|Array} [props.content] - ProseMirror content to render
|
|
33
|
+
* @param {string} [props.size='lg'] - Text size: sm, base, lg, xl, 2xl
|
|
34
|
+
* @param {string} [props.className] - Additional CSS classes
|
|
35
|
+
* @param {React.ReactNode} [props.children] - Content to render (alternative to content prop)
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* // With ProseMirror content
|
|
39
|
+
* <Article content={articleData.content} />
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* // With children
|
|
43
|
+
* <Article>
|
|
44
|
+
* <h1>My Article</h1>
|
|
45
|
+
* <p>Article content...</p>
|
|
46
|
+
* </Article>
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* // Composing with Render
|
|
50
|
+
* <Article size="base" className="dark:prose-invert">
|
|
51
|
+
* <Render content={proseMirrorContent} />
|
|
52
|
+
* </Article>
|
|
53
|
+
*/
|
|
54
|
+
export function Article({
|
|
55
|
+
content,
|
|
56
|
+
size = 'lg',
|
|
57
|
+
className,
|
|
58
|
+
children,
|
|
59
|
+
...props
|
|
60
|
+
}) {
|
|
61
|
+
const sizeClass = SIZE_CLASSES[size] || SIZE_CLASSES.lg
|
|
62
|
+
|
|
63
|
+
// Resolve content - if it's a ProseMirror doc, get the content array
|
|
64
|
+
let resolvedContent = content
|
|
65
|
+
if (resolvedContent?.type === 'doc') {
|
|
66
|
+
resolvedContent = resolvedContent.content
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return (
|
|
70
|
+
<article
|
|
71
|
+
className={cn('prose', sizeClass, 'max-w-none', className)}
|
|
72
|
+
{...props}
|
|
73
|
+
>
|
|
74
|
+
{children || (resolvedContent && <Render content={resolvedContent} />)}
|
|
75
|
+
</article>
|
|
76
|
+
)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export default Article
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prose Component
|
|
3
|
+
*
|
|
4
|
+
* Typography wrapper for long-form content.
|
|
5
|
+
* Applies prose styling for readable text.
|
|
6
|
+
*
|
|
7
|
+
* @module @uniweb/kit/styled/Prose
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import React from 'react'
|
|
11
|
+
import { cn } from '../../utils/index.js'
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Prose sizes
|
|
15
|
+
*/
|
|
16
|
+
const SIZE_CLASSES = {
|
|
17
|
+
sm: 'prose-sm',
|
|
18
|
+
base: 'prose-base',
|
|
19
|
+
lg: 'prose-lg',
|
|
20
|
+
xl: 'prose-xl',
|
|
21
|
+
'2xl': 'prose-2xl'
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Prose - Typography wrapper for long-form content
|
|
26
|
+
*
|
|
27
|
+
* Applies Tailwind Typography (prose) classes for readable text styling.
|
|
28
|
+
* Use for article bodies, documentation, or any long-form content.
|
|
29
|
+
*
|
|
30
|
+
* @param {Object} props
|
|
31
|
+
* @param {string} [props.size='lg'] - Text size: sm, base, lg, xl, 2xl
|
|
32
|
+
* @param {string} [props.as='div'] - HTML element to render as
|
|
33
|
+
* @param {string} [props.className] - Additional CSS classes
|
|
34
|
+
* @param {React.ReactNode} props.children - Content to render
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* <Prose>
|
|
38
|
+
* <h2>Article Title</h2>
|
|
39
|
+
* <p>Article content...</p>
|
|
40
|
+
* </Prose>
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* <Prose size="base" className="dark:prose-invert">
|
|
44
|
+
* <Render content={proseMirrorContent} />
|
|
45
|
+
* </Prose>
|
|
46
|
+
*/
|
|
47
|
+
export function Prose({
|
|
48
|
+
size = 'lg',
|
|
49
|
+
as: Component = 'div',
|
|
50
|
+
className,
|
|
51
|
+
children,
|
|
52
|
+
...props
|
|
53
|
+
}) {
|
|
54
|
+
const sizeClass = SIZE_CLASSES[size] || SIZE_CLASSES.lg
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<Component
|
|
58
|
+
className={cn('prose', sizeClass, 'max-w-none', className)}
|
|
59
|
+
{...props}
|
|
60
|
+
>
|
|
61
|
+
{children}
|
|
62
|
+
</Component>
|
|
63
|
+
)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export default Prose
|
package/src/styled/index.js
CHANGED
|
@@ -20,9 +20,15 @@ export { SidebarLayout } from './SidebarLayout/index.js'
|
|
|
20
20
|
// Content Rendering
|
|
21
21
|
// ============================================================================
|
|
22
22
|
|
|
23
|
-
// Section - Rich content section
|
|
23
|
+
// Section - Rich content section layout container
|
|
24
24
|
export { Section, Render } from './Section/index.js'
|
|
25
25
|
|
|
26
|
+
// Prose - Typography wrapper for long-form content
|
|
27
|
+
export { Prose } from './Prose/index.jsx'
|
|
28
|
+
|
|
29
|
+
// Article - Semantic article with prose typography
|
|
30
|
+
export { Article } from './Article/index.jsx'
|
|
31
|
+
|
|
26
32
|
// Renderers - Individual content type renderers
|
|
27
33
|
export { Code, Alert, Warning, Table, Details, Divider } from './Section/renderers/index.js'
|
|
28
34
|
|