@uniweb/kit 0.7.0 → 0.7.2

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.7.0",
3
+ "version": "0.7.2",
4
4
  "description": "Standard component library for Uniweb foundations",
5
5
  "type": "module",
6
6
  "exports": {
@@ -46,7 +46,7 @@
46
46
  "fuse.js": "^7.0.0",
47
47
  "shiki": "^3.0.0",
48
48
  "tailwind-merge": "^2.6.0",
49
- "@uniweb/core": "0.5.1"
49
+ "@uniweb/core": "0.5.3"
50
50
  },
51
51
  "peerDependencies": {
52
52
  "react": "^18.0.0 || ^19.0.0",
@@ -66,7 +66,7 @@ function renderList(items, ordered = false) {
66
66
  /**
67
67
  * Render a single content node
68
68
  */
69
- function RenderNode({ node, ...props }) {
69
+ function RenderNode({ node, block, ...props }) {
70
70
  if (!node) return null
71
71
 
72
72
  const { type, attrs, content } = node
@@ -203,7 +203,7 @@ function RenderNode({ node, ...props }) {
203
203
  return (
204
204
  <blockquote className="border-l-4 border-gray-300 pl-4 italic text-gray-600 my-4">
205
205
  {content?.map((child, i) => (
206
- <RenderNode key={i} node={child} />
206
+ <RenderNode key={i} node={child} block={block} />
207
207
  ))}
208
208
  </blockquote>
209
209
  )
@@ -239,6 +239,19 @@ function RenderNode({ node, ...props }) {
239
239
  return <Divider type={attrs?.type} className="my-6" />
240
240
  }
241
241
 
242
+ case 'inset_placeholder': {
243
+ const refId = attrs?.refId
244
+ if (!block || !refId) return null
245
+
246
+ const insetBlock = block.getInset(refId)
247
+ if (!insetBlock) return null
248
+
249
+ const InsetRenderer = insetBlock.getChildBlockRenderer()
250
+ if (!InsetRenderer) return null
251
+
252
+ return <InsetRenderer blocks={[insetBlock]} as="div" />
253
+ }
254
+
242
255
  case 'button': {
243
256
  const href = attrs?.href || '#'
244
257
  const label = extractText(node) || attrs?.label || 'Button'
@@ -286,7 +299,7 @@ function RenderNode({ node, ...props }) {
286
299
  return (
287
300
  <>
288
301
  {content.map((child, i) => (
289
- <RenderNode key={i} node={child} />
302
+ <RenderNode key={i} node={child} block={block} />
290
303
  ))}
291
304
  </>
292
305
  )
@@ -302,7 +315,7 @@ function RenderNode({ node, ...props }) {
302
315
  * @param {Array|Object} props.content - Content to render (array of nodes or single node)
303
316
  * @param {string} [props.className] - Additional CSS classes
304
317
  */
305
- export function Render({ content, className, ...props }) {
318
+ export function Render({ content, block, className, ...props }) {
306
319
  if (!content) return null
307
320
 
308
321
  const nodes = Array.isArray(content) ? content : [content]
@@ -310,7 +323,7 @@ export function Render({ content, className, ...props }) {
310
323
  return (
311
324
  <div className={cn('space-y-4', className)} {...props}>
312
325
  {nodes.map((node, i) => (
313
- <RenderNode key={i} node={node} />
326
+ <RenderNode key={i} node={node} block={block} />
314
327
  ))}
315
328
  </div>
316
329
  )
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Visual Component
3
+ *
4
+ * Renders the first visual element from content: inset > video > image.
5
+ * Used by section types with a visual slot (SplitContent, Showcase, etc.).
6
+ *
7
+ * Section types that declare `visuals: 1` (any type) should use this component.
8
+ * Section types that declare `visuals: 'image'` (media only) should use Media/Image directly.
9
+ *
10
+ * @module @uniweb/kit/styled/Visual
11
+ */
12
+
13
+ import React from 'react'
14
+ import { Media } from '../../components/Media/Media.jsx'
15
+ import { Image } from '../../components/Image/index.js'
16
+
17
+ /**
18
+ * Renders the first visual from content, checking insets first, then video, then image.
19
+ *
20
+ * @param {Object} props
21
+ * @param {Object} props.content - Parsed content from prepare-props
22
+ * @param {Object} props.block - Block instance (provides block.insets)
23
+ * @param {string} [props.className] - CSS classes for the visual container
24
+ * @param {React.ReactNode} [props.fallback] - Fallback when no visual is found
25
+ */
26
+ export function Visual({ content, block, className, fallback = null }) {
27
+ // Priority 1: Inset component (from @ComponentName in markdown)
28
+ const inset = block?.insets?.[0]
29
+ if (inset) {
30
+ const Renderer = inset.getChildBlockRenderer?.()
31
+ if (Renderer) {
32
+ return <Renderer blocks={[inset]} as="div" extra={{ className }} />
33
+ }
34
+ }
35
+
36
+ // Priority 2: Video
37
+ const video = content?.videos?.[0]
38
+ if (video) {
39
+ return <Media src={video.src || video} className={className} />
40
+ }
41
+
42
+ // Priority 3: Image
43
+ const img = content?.images?.[0] || content?.imgs?.[0]
44
+ if (img) {
45
+ return <Image src={img.src} alt={img.alt} className={className} />
46
+ }
47
+
48
+ return fallback
49
+ }
@@ -39,6 +39,9 @@ export { Code, Alert, Warning, Table, Details, Divider } from './Section/rendere
39
39
  // Disclaimer - Modal dialog for legal disclaimers
40
40
  export { Disclaimer } from './Disclaimer/index.js'
41
41
 
42
+ // Visual - Unified visual renderer (inset > video > image)
43
+ export { Visual } from './Visual/index.jsx'
44
+
42
45
  // Media - Video player with styled play button facade
43
46
  export { Media } from './Media/index.js'
44
47