@startupjs-ui/div 0.3.1 → 0.3.4

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/CHANGELOG.md CHANGED
@@ -3,6 +3,17 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [0.3.4](https://github.com/startupjs/startupjs-ui/compare/v0.3.3...v0.3.4) (2026-06-18)
7
+
8
+
9
+ ### Features
10
+
11
+ * **div, span:** supportTextNodes auto-wrap on Div + pure mode on Span ([#35](https://github.com/startupjs/startupjs-ui/issues/35)) ([d7bf159](https://github.com/startupjs/startupjs-ui/commit/d7bf159e73060a2531f2a1d66ec1932e9ddc3e6f))
12
+
13
+
14
+
15
+
16
+
6
17
  ## [0.3.1](https://github.com/startupjs/startupjs-ui/compare/v0.3.0...v0.3.1) (2026-06-08)
7
18
 
8
19
 
package/index.d.ts CHANGED
@@ -19,6 +19,13 @@ export interface DivProps extends Omit<ViewProps, 'role'> {
19
19
  style?: StyleProp<ViewStyle>;
20
20
  /** Content rendered inside Div */
21
21
  children?: ReactNode;
22
+ /** Auto-wrap bare text children. When true, runs of consecutive string/number
23
+ * children (including those inside arrays/fragments) are each wrapped into a
24
+ * single text node so they render correctly in a non-text container. @default false */
25
+ supportTextNodes?: boolean;
26
+ /** How to render an auto-wrapped text run (only used with supportTextNodes).
27
+ * Receives the merged text; should return a text element. Defaults to <Span/>. */
28
+ renderTextNode?: (text: string) => ReactNode;
22
29
  /** Visual feedback variant @default 'opacity' */
23
30
  variant?: 'opacity' | 'highlight';
24
31
  /** Render children in a horizontal row */
package/index.tsx CHANGED
@@ -1,4 +1,4 @@
1
- import { useContext, useLayoutEffect, useMemo, useState, useRef, type ReactNode, type RefObject } from 'react'
1
+ import { Children, cloneElement, createElement, Fragment, isValidElement, useContext, useLayoutEffect, useMemo, useState, useRef, type ReactNode, type RefObject } from 'react'
2
2
  import {
3
3
  View,
4
4
  Pressable,
@@ -17,6 +17,7 @@ import {
17
17
  mergeInheritedTextStyles,
18
18
  omitInheritedTextStyle
19
19
  } from '@startupjs-ui/span/textStyleContext'
20
+ import Span from '@startupjs-ui/span'
20
21
  import { useDecorateTooltipProps } from './useTooltip'
21
22
  import STYLES from './index.cssx.styl'
22
23
 
@@ -51,6 +52,13 @@ export interface DivProps extends Omit<ViewProps, 'role'> {
51
52
  style?: StyleProp<ViewStyle>
52
53
  /** Content rendered inside Div */
53
54
  children?: ReactNode
55
+ /** Auto-wrap bare text children. When true, runs of consecutive string/number
56
+ * children (including those inside arrays/fragments) are each wrapped into a
57
+ * single text node so they render correctly in a non-text container. @default false */
58
+ supportTextNodes?: boolean
59
+ /** How to render an auto-wrapped text run (only used with supportTextNodes).
60
+ * Receives the merged text; should return a text element. Defaults to <Span/>. */
61
+ renderTextNode?: (text: string) => ReactNode
54
62
  /** Visual feedback variant @default 'opacity' */
55
63
  variant?: 'opacity' | 'highlight'
56
64
  /** Render children in a horizontal row */
@@ -106,6 +114,8 @@ export interface DivProps extends Omit<ViewProps, 'role'> {
106
114
  function Div ({
107
115
  style: rawStyle = [],
108
116
  children,
117
+ supportTextNodes = false,
118
+ renderTextNode,
109
119
  variant = 'opacity',
110
120
  row,
111
121
  wrap,
@@ -131,6 +141,7 @@ function Div ({
131
141
  ...props
132
142
  }: DivProps): ReactNode {
133
143
  assertDeprecatedValues({ pushed, renderTooltip })
144
+ const renderedChildren = supportTextNodes ? wrapTextChildren(children, renderTextNode) : children
134
145
  let style = StyleSheet.flatten(rawStyle) as ViewStyle | undefined
135
146
  // on RN row-reverse switches margins and paddings sides, so we switch them back
136
147
  if (isNative && reverse) style = reverseMarginPaddingSides(style)
@@ -230,7 +241,7 @@ function Div ({
230
241
  ]
231
242
  accessible=accessible
232
243
  ...renderProps
233
- )= children
244
+ )= renderedChildren
234
245
  `
235
246
  const styledDivElement = nextInheritedTextStyle
236
247
  ? pug`
@@ -247,6 +258,35 @@ function Div ({
247
258
  } else return styledDivElement
248
259
  }
249
260
 
261
+ // Auto-wrap bare text so it renders in a non-text container. Each maximal run of
262
+ // consecutive string/number children is merged into one text node (so 'a {x} b'
263
+ // becomes one line, not three stacked nodes); element children break a run;
264
+ // fragments are traversed inline; arrays are already flattened by React.Children.
265
+ function wrapTextChildren (children: ReactNode, renderTextNode?: (text: string) => ReactNode): ReactNode {
266
+ const out: ReactNode[] = []
267
+ let run = ''
268
+ let key = 0
269
+ const flush = () => {
270
+ if (run === '') return
271
+ const text = run
272
+ run = ''
273
+ const node = renderTextNode ? renderTextNode(text) : createElement(Span, null, text)
274
+ out.push(isValidElement(node) ? cloneElement(node, { key: `__t${key++}` }) : node)
275
+ }
276
+ const walk = (nodes: ReactNode) => {
277
+ Children.forEach(nodes, child => {
278
+ if (child == null || typeof child === 'boolean') return
279
+ if (typeof child === 'string' || typeof child === 'number') { run += String(child); return }
280
+ if (isValidElement(child) && child.type === Fragment) { walk((child.props as { children?: ReactNode }).children); return }
281
+ flush()
282
+ out.push(isValidElement(child) && child.key == null ? cloneElement(child, { key: `__e${key++}` }) : child)
283
+ })
284
+ }
285
+ walk(children)
286
+ flush()
287
+ return out
288
+ }
289
+
250
290
  function isWebOnlyRole (role: unknown): role is Exclude<UIRole, ViewProps['role']> {
251
291
  return role === 'listbox' || role === 'gridcell'
252
292
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@startupjs-ui/div",
3
- "version": "0.3.1",
3
+ "version": "0.3.4",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -14,7 +14,7 @@
14
14
  "dependencies": {
15
15
  "@startupjs-ui/abstract-popover": "^0.3.0",
16
16
  "@startupjs-ui/core": "^0.3.0",
17
- "@startupjs-ui/span": "^0.3.1"
17
+ "@startupjs-ui/span": "^0.3.4"
18
18
  },
19
19
  "peerDependencies": {
20
20
  "react": "*",
@@ -22,5 +22,5 @@
22
22
  "react-native-reanimated": ">=4.0.0",
23
23
  "startupjs": "*"
24
24
  },
25
- "gitHead": "60311773bdc83f354c797a272774304502d28c58"
25
+ "gitHead": "ad84ad14d189280a9ce4618b3223093fd09ba60e"
26
26
  }