Skip to content

Commit e46635d

Browse files
authored
Merge pull request #95 from svelteplot/feat/axis-text-disabling
feat: allow disabling of axis labels using text={false} or text={null}
2 parents 3470b39 + dead08e commit e46635d

File tree

12 files changed

+133
-71
lines changed

12 files changed

+133
-71
lines changed

src/lib/marks/AxisX.svelte

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
tickClass?: ConstantAccessor<string>;
2626
/** ticks is a shorthand for defining data, tickCount or interval */
2727
ticks?: number | string | RawValue[];
28+
/** set to false or null to disable tick labels */
29+
text: boolean | null;
2830
} & XOR<
2931
{
3032
/** approximate number of ticks to be generated */
@@ -79,6 +81,7 @@
7981
class: className,
8082
tickCount = typeof magicTicks === 'number' ? magicTicks : undefined,
8183
tickSpacing,
84+
text = true,
8285
...options
8386
}: AxisXMarkProps = $props();
8487
@@ -220,6 +223,7 @@
220223
{tickPadding}
221224
{tickFontSize}
222225
{tickClass}
226+
{text}
223227
{options}
224228
title={useTitle}
225229
{plot} />

src/lib/marks/AxisY.svelte

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
tickClass?: ConstantAccessor<string>;
2727
/** ticks is a shorthand for defining data, tickCount or interval */
2828
ticks?: number | string | RawValue[];
29+
/** set to false or null to disable tick labels */
30+
text: boolean | null;
2931
} & XOR<
3032
{
3133
/** approximate number of ticks to be generated */
@@ -78,6 +80,7 @@
7880
tickClass,
7981
tickCount = typeof magicTicks === 'number' ? magicTicks : undefined,
8082
tickSpacing,
83+
text = true,
8184
...options
8285
}: AxisYMarkProps = $props();
8386
@@ -200,6 +203,7 @@
200203
{tickFontSize}
201204
{tickClass}
202205
{options}
206+
{text}
203207
title={useTitle}
204208
{plot} />
205209
{/if}

src/lib/marks/helpers/BaseAxisX.svelte

Lines changed: 59 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
dy: ConstantAccessor<number>;
3434
filter: ChannelAccessor;
3535
};
36+
text: boolean;
3637
plot: PlotState;
3738
};
3839
@@ -50,7 +51,8 @@
5051
height,
5152
options,
5253
plot,
53-
title
54+
title,
55+
text = true
5456
}: BaseAxisXProps = $props();
5557
5658
function splitTick(tick: string | string[]) {
@@ -86,22 +88,25 @@
8688
})
8789
);
8890
const T = tickObjects.length;
89-
for (let i = 0; i < T; i++) {
90-
let j = i;
91-
// find the preceding tick that was not hidden
92-
do {
93-
j--;
94-
} while (j >= 0 && tickObjects[j].hidden);
95-
if (j >= 0) {
96-
const tickLabelSpace = Math.abs(tickObjects[i].x - tickObjects[j].x);
97-
tickObjects[i].hidden = tickLabelSpace < 15;
91+
if (text) {
92+
for (let i = 0; i < T; i++) {
93+
let j = i;
94+
// find the preceding tick that was not hidden
95+
do {
96+
j--;
97+
} while (j >= 0 && tickObjects[j].hidden);
98+
if (j >= 0) {
99+
const tickLabelSpace = Math.abs(tickObjects[i].x - tickObjects[j].x);
100+
tickObjects[i].hidden = tickLabelSpace < 15;
101+
}
98102
}
99103
}
100104
return tickObjects;
101105
});
102106
103107
$effect(() => {
104108
untrack(() => [$autoMarginTop, $autoMarginBottom]);
109+
if (!text) return;
105110
const outsideTextAnchor = anchor === 'top' ? 'end' : 'start';
106111
// measure tick label heights
107112
const maxLabelHeight =
@@ -144,26 +149,7 @@
144149
<g class="axis-x">
145150
{#each positionedTicks as tick, t (t)}
146151
{#if testFilter(tick.value, options) && !tick.hidden}
147-
{@const textLines = tick.text}
148-
{@const prevTextLines = t && positionedTicks[t - 1].text}
149-
{@const fontSize = resolveProp(tickFontSize, tick)}
150152
{@const tickClass_ = resolveProp(tickClass, tick.value)}
151-
{@const estLabelWidth = max(textLines.map((t) => t.length)) * fontSize * 0.2}
152-
{@const moveDown =
153-
(tickSize + tickPadding + (tickRotate !== 0 ? tickFontSize * 0.35 : 0)) *
154-
(anchor === 'bottom' ? 1 : -1)}
155-
{@const [textStyle, textClass] = resolveStyles(
156-
plot,
157-
tick,
158-
{
159-
fontVariant: isQuantitative ? 'tabular-nums' : 'normal',
160-
...options,
161-
fontSize: tickFontSize,
162-
stroke: null
163-
},
164-
'fill',
165-
{ x: true }
166-
)}
167153
<g
168154
class="tick {tickClass_ || ''}"
169155
transform="translate({tick.x + tick.dx}, {tickY + tick.dy})"
@@ -182,31 +168,51 @@
182168
y2={anchor === 'bottom' ? tickSize : -tickSize} />
183169
{/if}
184170

185-
<text
186-
bind:this={tickTextElements[t]}
187-
transform="translate(0, {moveDown}) rotate({tickRotate})"
188-
style={textStyle}
189-
class={textClass}
190-
x={0}
191-
y={0}
192-
dominant-baseline={tickRotate !== 0
193-
? 'central'
194-
: anchor === 'bottom'
195-
? 'hanging'
196-
: 'auto'}>
197-
{#if ticks.length > 0 || t === 0 || t === ticks.length - 1}
198-
{#if textLines.length === 1}
199-
{textLines[0]}
200-
{:else}
201-
{#each textLines as line, i (i)}
202-
<tspan x="0" dy={i ? 12 : 0}
203-
>{!prevTextLines || prevTextLines[i] !== line
204-
? line
205-
: ''}</tspan>
206-
{/each}
171+
{#if text}
172+
{@const textLines = tick.text}
173+
{@const prevTextLines = t && positionedTicks[t - 1].text}
174+
175+
{@const moveDown =
176+
(tickSize + tickPadding + (tickRotate !== 0 ? tickFontSize * 0.35 : 0)) *
177+
(anchor === 'bottom' ? 1 : -1)}
178+
{@const [textStyle, textClass] = resolveStyles(
179+
plot,
180+
tick,
181+
{
182+
fontVariant: isQuantitative ? 'tabular-nums' : 'normal',
183+
...options,
184+
fontSize: tickFontSize,
185+
stroke: null
186+
},
187+
'fill',
188+
{ x: true }
189+
)}
190+
<text
191+
bind:this={tickTextElements[t]}
192+
transform="translate(0, {moveDown}) rotate({tickRotate})"
193+
style={textStyle}
194+
class={textClass}
195+
x={0}
196+
y={0}
197+
dominant-baseline={tickRotate !== 0
198+
? 'central'
199+
: anchor === 'bottom'
200+
? 'hanging'
201+
: 'auto'}>
202+
{#if ticks.length > 0 || t === 0 || t === ticks.length - 1}
203+
{#if textLines.length === 1}
204+
{textLines[0]}
205+
{:else}
206+
{#each textLines as line, i (i)}
207+
<tspan x="0" dy={i ? 12 : 0}
208+
>{!prevTextLines || prevTextLines[i] !== line
209+
? line
210+
: ''}</tspan>
211+
{/each}
212+
{/if}
207213
{/if}
208-
{/if}
209-
</text>
214+
</text>
215+
{/if}
210216
</g>
211217
{/if}
212218
{/each}

src/lib/marks/helpers/BaseAxisY.svelte

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
dy: ConstantAccessor<number>;
3030
};
3131
plot: PlotState;
32+
text: boolean | null;
3233
};
3334
3435
let {
@@ -46,7 +47,8 @@
4647
width,
4748
title,
4849
plot,
49-
options
50+
options,
51+
text = true
5052
}: BaseAxisYProps = $props();
5153
5254
const LINE_ANCHOR = {
@@ -67,16 +69,18 @@
6769
element: null as SVGTextElement | null
6870
};
6971
});
70-
const T = tickObjects.length;
71-
for (let i = 0; i < T; i++) {
72-
let j = i;
73-
// find the preceding tick that was not hidden
74-
do {
75-
j--;
76-
} while (j >= 0 && tickObjects[j].hidden);
77-
if (j >= 0) {
78-
const tickLabelSpace = Math.abs(tickObjects[i].y - tickObjects[j].y);
79-
tickObjects[i].hidden = tickLabelSpace < 15;
72+
if (text) {
73+
const T = tickObjects.length;
74+
for (let i = 0; i < T; i++) {
75+
let j = i;
76+
// find the preceding tick that was not hidden
77+
do {
78+
j--;
79+
} while (j >= 0 && tickObjects[j].hidden);
80+
if (j >= 0) {
81+
const tickLabelSpace = Math.abs(tickObjects[i].y - tickObjects[j].y);
82+
tickObjects[i].hidden = tickLabelSpace < 15;
83+
}
8084
}
8185
}
8286
return tickObjects;
@@ -176,13 +180,15 @@
176180
class={tickLineClass}
177181
x2={anchor === 'left' ? -tickSize : tickSize} />
178182
{/if}
179-
<text
180-
bind:this={tickTexts[t]}
181-
class={[textClass, { 'is-left': anchor === 'left' }]}
182-
style={textStyle}
183-
x={(tickSize + tickPadding) * (anchor === 'left' ? -1 : 1)}
184-
dominant-baseline={LINE_ANCHOR[lineAnchor]}
185-
>{Array.isArray(tick.text) ? tick.text.join(' ') : tick.text}</text>
183+
{#if text}
184+
<text
185+
bind:this={tickTexts[t]}
186+
class={[textClass, { 'is-left': anchor === 'left' }]}
187+
style={textStyle}
188+
x={(tickSize + tickPadding) * (anchor === 'left' ? -1 : 1)}
189+
dominant-baseline={LINE_ANCHOR[lineAnchor]}
190+
>{Array.isArray(tick.text) ? tick.text.join(' ') : tick.text}</text>
191+
{/if}
186192
</g>
187193
{/if}
188194
{/each}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<script module>
2+
export const title = 'Major and minor ticks';
3+
export const description =
4+
'You can add minor ticks lines by adding a second axis and disabling the text labels.';
5+
</script>
6+
7+
<script>
8+
import { Plot, AxisX, AxisY, Line } from 'svelteplot';
9+
import { page } from '$app/state';
10+
let { aapl } = $derived(page.data.data);
11+
</script>
12+
13+
<Plot grid inset={10}>
14+
<AxisX />
15+
<AxisX interval="1 month" text={false} tickSize={3} />
16+
<AxisY />
17+
<AxisY interval={5} text={false} tickSize={3} />
18+
<Line data={aapl} x="Date" y="Close" />
19+
</Plot>

src/tests/axisX.test.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,4 +153,27 @@ describe('AxisX mark', () => {
153153
expect(ticks.length).toBe(5);
154154
expect(tickValues).toStrictEqual(['Jan2000', 'Jul', 'Jan2001', 'Jul', 'Jan2002']);
155155
});
156+
157+
it('disable tick texts', () => {
158+
const { container } = render(AxisXTest, {
159+
props: {
160+
plotArgs: {
161+
width: 400,
162+
x: { domain: [new Date(2000, 0, 1), new Date(2002, 0, 1)] }
163+
},
164+
axisArgs: { ticks: '1 month', text: null }
165+
}
166+
});
167+
168+
const ticks = container.querySelectorAll('g.axis-x > g.tick') as NodeListOf<SVGGElement>;
169+
const tickLines = container.querySelectorAll(
170+
'g.axis-x > g.tick line'
171+
) as NodeListOf<SVGLineElement>;
172+
const tickLabels = container.querySelectorAll(
173+
'g.axis-x > g.tick text'
174+
) as NodeListOf<SVGTextElement>;
175+
expect(ticks.length).toBe(25);
176+
expect(tickLines.length).toBe(25);
177+
expect(tickLabels.length).toBe(0);
178+
});
156179
});
38.3 KB
Loading

static/examples/axis/major-minor.png

37.1 KB
Loading
892 Bytes
Loading

static/examples/axis/tick-spacing.png

939 Bytes
Loading

0 commit comments

Comments
 (0)