@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uniweb/kit",
3
- "version": "0.1.8",
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.13"
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
- * SSG-safe hook for active route detection in navigation components.
5
- * Provides utilities for checking if pages are active or ancestors of the current route.
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.label}
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
- * Normalize a route by removing leading/trailing slashes
26
- * @param {string} route
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 route = normalizeRoute(location?.pathname)
47
- const rootSegment = route.split('/')[0]
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} page - Page object with getNormalizedRoute() or route property
62
+ * @param {Object|string} pageOrRoute - Page object or route string
67
63
  * @returns {boolean}
68
64
  */
69
- isActive: (page) => {
70
- if (typeof page.getNormalizedRoute === 'function') {
71
- return page.getNormalizedRoute() === route
72
- }
73
- // Fallback for page info objects from getPageHierarchy
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} page - Page object with isActiveOrAncestor() or route property
77
+ * @param {Object|string} pageOrRoute - Page object or route string
82
78
  * @returns {boolean}
83
79
  */
84
- isActiveOrAncestor: (page) => {
85
- if (typeof page.isActiveOrAncestor === 'function') {
86
- return page.isActiveOrAncestor(route)
87
- }
88
- // Fallback for page info objects from getPageHierarchy
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
  }
@@ -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 (!uniweb) {
24
- console.warn('[Kit] Runtime not initialized. Components require @uniweb/runtime.')
25
- return {
26
- website: null,
27
- localize: (val, defaultVal = '') => defaultVal,
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
@@ -20,9 +20,15 @@ export { SidebarLayout } from './SidebarLayout/index.js'
20
20
  // Content Rendering
21
21
  // ============================================================================
22
22
 
23
- // Section - Rich content section renderer
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