@startupjs-ui/div 0.3.0 → 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 +22 -0
- package/index.d.ts +7 -0
- package/index.tsx +76 -8
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,28 @@
|
|
|
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
|
+
|
|
17
|
+
## [0.3.1](https://github.com/startupjs/startupjs-ui/compare/v0.3.0...v0.3.1) (2026-06-08)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
### Features
|
|
21
|
+
|
|
22
|
+
* support inherited text styles from Div, support relative lineHeight ([#32](https://github.com/startupjs/startupjs-ui/issues/32)) ([47b56ab](https://github.com/startupjs/startupjs-ui/commit/47b56abca03b1d3ef1d977309deffd95a2de709d))
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
6
28
|
# [0.3.0](https://github.com/startupjs/startupjs-ui/compare/v0.2.3...v0.3.0) (2026-05-27)
|
|
7
29
|
|
|
8
30
|
|
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 { useLayoutEffect, 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,
|
|
@@ -11,6 +11,13 @@ import {
|
|
|
11
11
|
import Animated from 'react-native-reanimated'
|
|
12
12
|
import { pug, observer, u, useDidUpdate } from 'startupjs'
|
|
13
13
|
import { colorToRGBA, getCssVariable, themed, type UIRole } from '@startupjs-ui/core'
|
|
14
|
+
import {
|
|
15
|
+
TextStyleContext,
|
|
16
|
+
getInheritedTextStyle,
|
|
17
|
+
mergeInheritedTextStyles,
|
|
18
|
+
omitInheritedTextStyle
|
|
19
|
+
} from '@startupjs-ui/span/textStyleContext'
|
|
20
|
+
import Span from '@startupjs-ui/span'
|
|
14
21
|
import { useDecorateTooltipProps } from './useTooltip'
|
|
15
22
|
import STYLES from './index.cssx.styl'
|
|
16
23
|
|
|
@@ -45,6 +52,13 @@ export interface DivProps extends Omit<ViewProps, 'role'> {
|
|
|
45
52
|
style?: StyleProp<ViewStyle>
|
|
46
53
|
/** Content rendered inside Div */
|
|
47
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
|
|
48
62
|
/** Visual feedback variant @default 'opacity' */
|
|
49
63
|
variant?: 'opacity' | 'highlight'
|
|
50
64
|
/** Render children in a horizontal row */
|
|
@@ -98,8 +112,10 @@ export interface DivProps extends Omit<ViewProps, 'role'> {
|
|
|
98
112
|
}
|
|
99
113
|
|
|
100
114
|
function Div ({
|
|
101
|
-
style = [],
|
|
115
|
+
style: rawStyle = [],
|
|
102
116
|
children,
|
|
117
|
+
supportTextNodes = false,
|
|
118
|
+
renderTextNode,
|
|
103
119
|
variant = 'opacity',
|
|
104
120
|
row,
|
|
105
121
|
wrap,
|
|
@@ -125,9 +141,20 @@ function Div ({
|
|
|
125
141
|
...props
|
|
126
142
|
}: DivProps): ReactNode {
|
|
127
143
|
assertDeprecatedValues({ pushed, renderTooltip })
|
|
128
|
-
|
|
144
|
+
const renderedChildren = supportTextNodes ? wrapTextChildren(children, renderTextNode) : children
|
|
145
|
+
let style = StyleSheet.flatten(rawStyle) as ViewStyle | undefined
|
|
129
146
|
// on RN row-reverse switches margins and paddings sides, so we switch them back
|
|
130
147
|
if (isNative && reverse) style = reverseMarginPaddingSides(style)
|
|
148
|
+
|
|
149
|
+
const inheritedTextStyle = useContext(TextStyleContext)
|
|
150
|
+
const ownTextStyle = getInheritedTextStyle(style)
|
|
151
|
+
const nextInheritedTextStyleKey = simpleNumericHash(JSON.stringify([inheritedTextStyle, ownTextStyle]))
|
|
152
|
+
const nextInheritedTextStyle = useMemo(() => {
|
|
153
|
+
if (!ownTextStyle) return undefined
|
|
154
|
+
return mergeInheritedTextStyles(inheritedTextStyle, ownTextStyle)
|
|
155
|
+
}, [nextInheritedTextStyleKey]) // eslint-disable-line react-hooks/exhaustive-deps
|
|
156
|
+
omitInheritedTextStyle(style)
|
|
157
|
+
|
|
131
158
|
if (gap === true) gap = 2
|
|
132
159
|
const isPressable = hasPressHandler(props)
|
|
133
160
|
const fallbackRef = useRef<any>(null)
|
|
@@ -214,15 +241,50 @@ function Div ({
|
|
|
214
241
|
]
|
|
215
242
|
accessible=accessible
|
|
216
243
|
...renderProps
|
|
217
|
-
)=
|
|
244
|
+
)= renderedChildren
|
|
218
245
|
`
|
|
246
|
+
const styledDivElement = nextInheritedTextStyle
|
|
247
|
+
? pug`
|
|
248
|
+
TextStyleContext.Provider(value=nextInheritedTextStyle)
|
|
249
|
+
= divElement
|
|
250
|
+
`
|
|
251
|
+
: divElement
|
|
219
252
|
|
|
220
253
|
if (tooltipElement) {
|
|
221
254
|
return pug`
|
|
222
|
-
=
|
|
255
|
+
= styledDivElement
|
|
223
256
|
= tooltipElement
|
|
224
257
|
`
|
|
225
|
-
} else return
|
|
258
|
+
} else return styledDivElement
|
|
259
|
+
}
|
|
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
|
|
226
288
|
}
|
|
227
289
|
|
|
228
290
|
function isWebOnlyRole (role: unknown): role is Exclude<UIRole, ViewProps['role']> {
|
|
@@ -436,8 +498,8 @@ function hasPressHandler (props: Record<string, any>): boolean {
|
|
|
436
498
|
return PRESSABLE_PROPS.some(prop => props[prop])
|
|
437
499
|
}
|
|
438
500
|
|
|
439
|
-
function reverseMarginPaddingSides (style:
|
|
440
|
-
style
|
|
501
|
+
function reverseMarginPaddingSides (style: ViewStyle | undefined): ViewStyle | undefined {
|
|
502
|
+
if (!style) return style
|
|
441
503
|
const { paddingLeft, paddingRight, marginLeft, marginRight } = style
|
|
442
504
|
style.marginLeft = marginRight
|
|
443
505
|
style.marginRight = marginLeft
|
|
@@ -446,6 +508,12 @@ function reverseMarginPaddingSides (style: StyleProp<ViewStyle>) {
|
|
|
446
508
|
return style
|
|
447
509
|
}
|
|
448
510
|
|
|
511
|
+
function simpleNumericHash (s: string): number {
|
|
512
|
+
let h = 0
|
|
513
|
+
for (let i = 0; i < s.length; i++) h = Math.imul(31, h) + s.charCodeAt(i) | 0
|
|
514
|
+
return h
|
|
515
|
+
}
|
|
516
|
+
|
|
449
517
|
function assertDeprecatedValues ({ pushed, renderTooltip }: { pushed?: any, renderTooltip?: any }) {
|
|
450
518
|
if (DEPRECATED_PUSHED_VALUES.includes(pushed)) console.warn(ERRORS.DEPRECATED_PUSHED(pushed))
|
|
451
519
|
if (renderTooltip) console.warn(ERRORS.DEPRECATED_RENDER_TOOLTIP)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@startupjs-ui/div",
|
|
3
|
-
"version": "0.3.
|
|
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.
|
|
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": "
|
|
25
|
+
"gitHead": "ad84ad14d189280a9ce4618b3223093fd09ba60e"
|
|
26
26
|
}
|