Skip to content

Commit 781f21e

Browse files
committed
feat(Text): Add segments prop to Text component for inline mixed-style text
1 parent b538c96 commit 781f21e

9 files changed

Lines changed: 236 additions & 81 deletions

File tree

.changeset/add-text-segments.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'layerchart': minor
3+
---
4+
5+
feat(Text): Add `segments` prop for inline mixed-style text
6+
7+
New `segments` prop accepts an array of `{ value, class }` objects to render text with different styles (font size, weight, color) inline. Works across SVG (via tspans), Canvas (via sequential measureText/fillText), and HTML layers. Useful for labels that combine a bold name with a lighter value, such as treemap headers.

docs/src/content/components/Text.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@ Use a threshold scale to color text based on value ranges.
3131

3232
:example{ name="color-via-threshold-scale" showCode }
3333

34+
### Segments
35+
36+
Use the `segments` prop to render inline text with mixed styles (different font sizes, weights, or colors). Each segment gets its own `class` for styling. Works across SVG, Canvas, and HTML layers.
37+
38+
:example{ name="segments" showCode }
39+
3440
## Use cases
3541

3642
### Truncate text of axis labels
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<script lang="ts">
2+
import { Chart, Layer, Text } from 'layerchart';
3+
</script>
4+
5+
<Chart height={80}>
6+
<Layer>
7+
<Text
8+
segments={[
9+
{ value: 'Revenue', class: 'text-sm font-semibold fill-primary' },
10+
{ value: ' $1,200', class: 'text-xs font-light fill-surface-content/75' }
11+
]}
12+
x={0}
13+
y={20}
14+
/>
15+
<Text
16+
segments={[
17+
{ value: 'Growth', class: 'text-sm font-semibold fill-primary' },
18+
{ value: ' +12%', class: 'text-xs font-light fill-success' }
19+
]}
20+
x={0}
21+
y={50}
22+
/>
23+
</Layer>
24+
</Chart>

docs/src/examples/components/Treemap/complex.svelte

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -109,21 +109,36 @@
109109
rx={5}
110110
/>
111111
<RectClipPath width={nodeWidth} height={nodeHeight}>
112-
<text
112+
<Text
113+
segments={[
114+
{
115+
value: node.data.name,
116+
class: cls(
117+
'text-[10px] font-medium',
118+
config.colorBy === 'children'
119+
? 'fill-primary-content'
120+
: 'fill-black'
121+
),
122+
},
123+
...(node.children
124+
? [
125+
{
126+
value: ` ${format(node.value ?? 0, 'integer')}`,
127+
class: cls(
128+
'text-[8px] font-extralight',
129+
config.colorBy === 'children'
130+
? 'fill-primary-content'
131+
: 'fill-black'
132+
),
133+
},
134+
]
135+
: []),
136+
]}
137+
verticalAnchor="start"
138+
lineHeight="10px"
113139
x={4}
114-
y={16 * 0.6 + 4}
115-
class={cls(
116-
'text-[10px] font-medium',
117-
config.colorBy === 'children' ? 'fill-primary-content' : 'fill-black'
118-
)}
119-
>
120-
<tspan>{node.data.name}</tspan>
121-
{#if node.children}
122-
<tspan class="text-[8px] font-extralight">
123-
{format(node.value ?? 0, 'integer')}
124-
</tspan>
125-
{/if}
126-
</text>
140+
y={3.6}
141+
/>
127142

128143
{#if !node.children}
129144
<Text
@@ -133,6 +148,7 @@
133148
config.colorBy === 'children' ? 'fill-primary-content' : 'fill-black'
134149
)}
135150
verticalAnchor="start"
151+
lineHeight="8px"
136152
x={4}
137153
y={16}
138154
/>

docs/src/examples/components/Treemap/nested-filter.svelte

Lines changed: 42 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
import { Button, Breadcrumb } from 'svelte-ux';
1616
import { format } from '@layerstack/utils';
1717
import { cls } from '@layerstack/tailwind';
18-
import { Chart, Group, Rect, RectClipPath, Layer, Treemap, findAncestor } from 'layerchart';
18+
import { Chart, Group, Rect, RectClipPath, Layer, Text, Treemap, findAncestor } from 'layerchart';
1919
2020
let config = $state({
2121
tile: 'squarify' as ComponentProps<typeof Treemap>['tile'],
@@ -160,29 +160,49 @@
160160
height={nodeHeight}
161161
motion={{ type: 'tween', delay: 600 }}
162162
>
163-
<text
163+
<Text
164+
segments={[
165+
{
166+
value: node.data[0] ?? 'Overall',
167+
class: cls(
168+
'text-[10px] font-medium',
169+
config.colorBy === 'children'
170+
? 'fill-primary-content'
171+
: 'fill-black'
172+
),
173+
},
174+
...(node.children
175+
? [
176+
{
177+
value: ` ${format(node.value ?? 0, 'integer')}`,
178+
class: cls(
179+
'text-[8px] font-extralight',
180+
config.colorBy === 'children'
181+
? 'fill-primary-content'
182+
: 'fill-black'
183+
),
184+
},
185+
]
186+
: []),
187+
]}
188+
verticalAnchor="start"
189+
lineHeight="10px"
164190
x={4}
165-
y={16 * 0.6 + 4}
166-
class={cls(
167-
'text-[10px] font-medium',
168-
config.colorBy === 'children' ? 'fill-primary-content' : 'fill-black'
169-
)}
170-
>
171-
<tspan>{node.data[0] ?? 'Overall'}</tspan>
172-
{#if node.children}
173-
<tspan class="text-[8px] font-extralight"
174-
>{format(node.value ?? 0, 'integer')}</tspan
175-
>
176-
{/if}
177-
</text>
191+
y={3.6}
192+
/>
193+
178194
{#if !node.children}
179-
<!-- <Text
180-
value={format(node.value ?? 0, 'integer')}
181-
class="text-[8px] font-extralight"
182-
verticalAnchor="start"
183-
x={4}
184-
y={16}
185-
/> -->
195+
<Text
196+
value={format(node.value ?? 0, 'integer')}
197+
class={cls(
198+
'text-[8px] font-extralight',
199+
config.colorBy === 'children' ? 'fill-primary-content' : 'fill-black'
200+
)}
201+
verticalAnchor="start"
202+
lineHeight="8px"
203+
x={4}
204+
y={16}
205+
/>
186206
{/if}
187207
</RectClipPath>
188208
</Group>

docs/src/examples/components/Treemap/nested-zoom.svelte

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -126,21 +126,37 @@
126126
rx={5}
127127
/>
128128
<RectClipPath width={nodeWidth} height={nodeHeight}>
129-
<text
129+
<Text
130+
segments={[
131+
{
132+
value: node.data.name,
133+
class: cls(
134+
'text-[10px] font-medium',
135+
config.colorBy === 'children'
136+
? 'fill-primary-content'
137+
: 'fill-black'
138+
),
139+
},
140+
...(node.children
141+
? [
142+
{
143+
value: ` ${format(node.value ?? 0, 'integer')}`,
144+
class: cls(
145+
'text-[8px] font-extralight',
146+
config.colorBy === 'children'
147+
? 'fill-primary-content'
148+
: 'fill-black'
149+
),
150+
},
151+
]
152+
: []),
153+
]}
154+
verticalAnchor="start"
155+
lineHeight="10px"
130156
x={4}
131-
y={16 * 0.6 + 4}
132-
class={cls(
133-
'text-[10px] font-medium',
134-
config.colorBy === 'children' ? 'fill-primary-content' : 'fill-black'
135-
)}
136-
>
137-
<tspan>{node.data.name}</tspan>
138-
{#if node.children}
139-
<tspan class="text-[8px] font-extralight">
140-
{format(node.value ?? 0, 'integer')}
141-
</tspan>
142-
{/if}
143-
</text>
157+
y={3.6}
158+
/>
159+
144160
{#if !node.children}
145161
<Text
146162
value={format(node.value ?? 0, 'integer')}
@@ -149,6 +165,7 @@
149165
config.colorBy === 'children' ? 'fill-primary-content' : 'fill-black'
150166
)}
151167
verticalAnchor="start"
168+
lineHeight="8px"
152169
x={4}
153170
y={16}
154171
/>

docs/src/examples/components/Treemap/playground.svelte

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -151,21 +151,36 @@
151151
rx={5}
152152
/>
153153
<RectClipPath width={nodeWidth} height={nodeHeight}>
154-
<text
154+
<Text
155+
segments={[
156+
{
157+
value: node.data.name,
158+
class: cls(
159+
'text-[10px] font-medium',
160+
config.colorBy === 'children'
161+
? 'fill-primary-content'
162+
: 'fill-black'
163+
),
164+
},
165+
...(node.children
166+
? [
167+
{
168+
value: ` ${format(node.value ?? 0, 'integer')}`,
169+
class: cls(
170+
'text-[8px] font-extralight',
171+
config.colorBy === 'children'
172+
? 'fill-primary-content'
173+
: 'fill-black'
174+
),
175+
},
176+
]
177+
: []),
178+
]}
179+
verticalAnchor="start"
180+
lineHeight="10px"
155181
x={4}
156-
y={16 * 0.6 + 4}
157-
class={cls(
158-
'text-[10px] font-medium',
159-
config.colorBy === 'children' ? 'fill-primary-content' : 'fill-black'
160-
)}
161-
>
162-
<tspan>{node.data.name}</tspan>
163-
{#if node.children}
164-
<tspan class="text-[8px] font-extralight">
165-
{format(node.value ?? 0, 'integer')}
166-
</tspan>
167-
{/if}
168-
</text>
182+
y={3.6}
183+
/>
169184

170185
{#if !node.children}
171186
<Text
@@ -175,6 +190,7 @@
175190
config.colorBy === 'children' ? 'fill-primary-content' : 'fill-black'
176191
)}
177192
verticalAnchor="start"
193+
lineHeight="8px"
178194
x={4}
179195
y={16}
180196
/>

docs/src/examples/components/Treemap/stacked-zoom.svelte

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,8 +119,9 @@
119119
config.colorBy === 'children' ? 'fill-primary-content' : 'fill-black'
120120
)}
121121
verticalAnchor="start"
122+
lineHeight="10px"
122123
x={4}
123-
y={2}
124+
y={3.6}
124125
/>
125126
<Text
126127
value={format(node.value ?? 0, 'integer')}
@@ -129,6 +130,7 @@
129130
config.colorBy === 'children' ? 'fill-primary-content' : 'fill-black'
130131
)}
131132
verticalAnchor="start"
133+
lineHeight="8px"
132134
x={4}
133135
y={16}
134136
/>

0 commit comments

Comments
 (0)