Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 25 additions & 7 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ interface WrapperElement extends HTMLElement {
type RelayoutFn = (
id: string | number,
ratio: number,
maxScale: number,
wrapper?: WrapperElement
) => void

Expand All @@ -25,15 +26,16 @@ declare global {
}
}

const relayout: RelayoutFn = (id, ratio, wrapper) => {
const relayout: RelayoutFn = (id, ratio, maxScale, wrapper) => {
wrapper =
wrapper || document.querySelector<WrapperElement>(`[data-br="${id}"]`)
const container = wrapper.parentElement

const update = (width: number) => (wrapper.style.maxWidth = width + 'px')

// Reset wrapper width
// Reset wrapper width & scale
wrapper.style.maxWidth = ''
wrapper.style.fontSize = ''

// Get the initial container size
const width = container.clientWidth
Expand All @@ -56,15 +58,24 @@ const relayout: RelayoutFn = (id, ratio, wrapper) => {
}

// Update the wrapper width
update(upper * ratio + width * (1 - ratio))
let maxWidth = upper * ratio + width * (1 - ratio)

// Update the font scale
if (maxScale > 1 && maxWidth + 1 < width) {
const scale = Math.min(maxScale, width / (maxWidth + 1))
wrapper.style.fontSize = scale + 'em'
maxWidth *= scale
}

update(maxWidth)
}

// Create a new observer if we don't have one.
// Note that we must inline the key here as we use `toString()` to serialize
// the function.
if (!wrapper['__wrap_o']) {
;(wrapper['__wrap_o'] = new ResizeObserver(() => {
self.__wrap_b(0, +wrapper.dataset.brr, wrapper)
self.__wrap_b(0, +wrapper.dataset.brr, +wrapper.dataset.brs, wrapper)
})).observe(container)
}
}
Expand Down Expand Up @@ -94,6 +105,11 @@ interface BalancerProps extends React.HTMLAttributes<HTMLElement> {
* @default 1
*/
ratio?: number
/**
* The maximum scale to apply to the font-size to fit the container width.
* @default 1
*/
maxScale?: number
children?: React.ReactNode
}

Expand All @@ -116,6 +132,7 @@ const Provider: React.FC<{
const Balancer: React.FC<BalancerProps> = ({
as: Wrapper = 'span',
ratio = 1,
maxScale = 1,
children,
...props
}) => {
Expand All @@ -127,9 +144,9 @@ const Balancer: React.FC<BalancerProps> = ({
useIsomorphicLayoutEffect(() => {
if (wrapperRef.current) {
// Re-assign the function here as the component can be dynamically rendered, and script tag won't work in that case.
;(self[SYMBOL_KEY] = relayout)(0, ratio, wrapperRef.current)
;(self[SYMBOL_KEY] = relayout)(0, ratio, maxScale, wrapperRef.current)
}
}, [children, ratio])
}, [children, ratio, maxScale])

// Remove the observer when unmounting.
useIsomorphicLayoutEffect(() => {
Expand Down Expand Up @@ -172,6 +189,7 @@ To:
{...props}
data-br={id}
data-brr={ratio}
data-brs={maxScale}
ref={wrapperRef}
style={{
display: 'inline-block',
Expand Down Expand Up @@ -204,7 +222,7 @@ if (!IS_SERVER && process.env.NODE_ENV !== 'production') {
document.querySelectorAll<WrapperElement>('[data-br]')

for (const element of Array.from(elements)) {
self[SYMBOL_KEY](0, +element.dataset.brr, element)
self[SYMBOL_KEY](0, +element.dataset.brr, +element.dataset.brs, element)
}
}
}
Expand Down
1 change: 1 addition & 0 deletions website/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export default function HomePage() {
<Section.Hero />
<Section.GettingStarted />
<Section.CustomBalanceRatio />
<Section.CustomMaxScale />
<Section.HowItWorks />
<Section.UseCases />
<Section.Performance />
Expand Down
6 changes: 4 additions & 2 deletions website/app/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -400,14 +400,16 @@ ul {
padding-left: 18px;
}

h2.ratio-title {
h2.ratio-title,
h2.scale-title {
position: relative;
margin-bottom: 1em;
background: #fff2e0;
border-right: 1px dashed #c59759;
border-left: 1px dashed #c59759;
}
h2.ratio-title span {
h2.ratio-title span,
h2.scale-title span {
background: #cdebff;
}
h2.ratio-title:after,
Expand Down
59 changes: 59 additions & 0 deletions website/src/sections/CustomMaxScale.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
'use client'

import { useSpring } from '@react-spring/web'
import { useState } from 'react'
import Balancer from 'react-wrap-balancer'

export default function CustomMaxScale() {
const [scale, setScale] = useState<number>(1.5)
const [currentScale, setCurrentScale] = useState<number>(1.5)

const handleRange: React.ChangeEventHandler<HTMLInputElement> = (event) => {
setScale(+event.target.value / 100 + 1)
}

useSpring({
from: { r: 1.5 },
to: { r: scale },
onChange(prop) {
setCurrentScale(prop.value.r)
},
})

return (
<>
<p className='headline'>
<Balancer>Custom Max Scale</Balancer>
</p>
<div className='demo-container'>
<div className='controller'>
<input type='range' defaultValue='50' onChange={handleRange} />
</div>
<div className='demo' style={{ width: 480, maxWidth: '100%' }}>
<div
style={{
textAlign: 'center',
position: 'relative',
}}
>
<div>
<h2 className='scale-title'>
<Balancer maxScale={currentScale}>
The quick brown fox jumps over the lazy dog
</Balancer>
</h2>
<code>{`<Balancer maxScale={${scale.toFixed(2)}}>`}</code>
</div>
</div>
</div>
</div>
<h3>
<Balancer>
Adjust the max scale to a custom value between{' '}
<span className='code'>1</span> (no scaling, the default) and{' '}
<span className='code'>2</span> (twice the authored font-size)
</Balancer>
</h3>
</>
)
}
3 changes: 3 additions & 0 deletions website/src/sections/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { default as About } from './About'
import { default as CustomBalanceRatio } from './CustomBalanceRatio'
import { default as CustomMaxScale } from './CustomMaxScale'
import { default as GettingStarted } from './GettingStarted'
import { default as Header } from './Header'
import { default as Footer } from './Footer'
Expand All @@ -10,6 +11,7 @@ import { default as UseCases } from './UseCases'
export {
About,
CustomBalanceRatio,
CustomMaxScale,
GettingStarted,
Header,
Footer,
Expand All @@ -21,6 +23,7 @@ export {
const Sections = {
About,
CustomBalanceRatio,
CustomMaxScale,
GettingStarted,
Header,
Footer,
Expand Down