mirror of
https://github.com/gpxstudio/gpx.studio.git
synced 2025-09-01 08:12:32 +00:00
toggle additional data on elevation profile
This commit is contained in:
@@ -397,6 +397,22 @@ export class TrackPoint {
|
||||
getCoordinates(): Coordinates {
|
||||
return this.attributes;
|
||||
}
|
||||
|
||||
getHeartRate(): number {
|
||||
return this.extensions && this.extensions['gpxtpx:TrackPointExtension'] && this.extensions['gpxtpx:TrackPointExtension']['gpxtpx:hr'] ? this.extensions['gpxtpx:TrackPointExtension']['gpxtpx:hr'] : undefined;
|
||||
}
|
||||
|
||||
getCadence(): number {
|
||||
return this.extensions && this.extensions['gpxtpx:TrackPointExtension'] && this.extensions['gpxtpx:TrackPointExtension']['gpxtpx:cad'] ? this.extensions['gpxtpx:TrackPointExtension']['gpxtpx:cad'] : undefined;
|
||||
}
|
||||
|
||||
getTemperature(): number {
|
||||
return this.extensions && this.extensions['gpxtpx:TrackPointExtension'] && this.extensions['gpxtpx:TrackPointExtension']['gpxtpx:atemp'] ? this.extensions['gpxtpx:TrackPointExtension']['gpxtpx:atemp'] : undefined;
|
||||
}
|
||||
|
||||
getPower(): number {
|
||||
return this.extensions && this.extensions["gpxpx:PowerExtension"] && this.extensions["gpxpx:PowerExtension"]["gpxpx:PowerInWatts"] ? this.extensions["gpxpx:PowerExtension"]["gpxpx:PowerInWatts"] : undefined;
|
||||
}
|
||||
};
|
||||
|
||||
export class Waypoint {
|
||||
|
8
website/package-lock.json
generated
8
website/package-lock.json
generated
@@ -9,7 +9,7 @@
|
||||
"version": "0.0.1",
|
||||
"dependencies": {
|
||||
"@mapbox/mapbox-gl-geocoder": "^5.0.2",
|
||||
"bits-ui": "^0.21.3",
|
||||
"bits-ui": "^0.21.4",
|
||||
"chart.js": "^4.4.2",
|
||||
"clsx": "^2.1.0",
|
||||
"gpx": "file:../gpx",
|
||||
@@ -1828,9 +1828,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/bits-ui": {
|
||||
"version": "0.21.3",
|
||||
"resolved": "https://registry.npmjs.org/bits-ui/-/bits-ui-0.21.3.tgz",
|
||||
"integrity": "sha512-VMQVXwYIjYmDoudIRm2ZlS2guy97lUQk73DwSfTnaS0dhldImbDFMATNxjLSLsTDj8FqJ8Dv78wSctdxcloIbQ==",
|
||||
"version": "0.21.4",
|
||||
"resolved": "https://registry.npmjs.org/bits-ui/-/bits-ui-0.21.4.tgz",
|
||||
"integrity": "sha512-IL+7s19GW561jwkeYk23dwkTfQ9606I062qqv2AtjCdhhIdoOEJNVBX0kjP5xefSaS6ojL0HGG54att0aRTcAQ==",
|
||||
"dependencies": {
|
||||
"@internationalized/date": "^3.5.1",
|
||||
"@melt-ui/svelte": "0.76.2",
|
||||
|
@@ -42,7 +42,7 @@
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@mapbox/mapbox-gl-geocoder": "^5.0.2",
|
||||
"bits-ui": "^0.21.3",
|
||||
"bits-ui": "^0.21.4",
|
||||
"chart.js": "^4.4.2",
|
||||
"clsx": "^2.1.0",
|
||||
"gpx": "file:../gpx",
|
||||
|
@@ -1,88 +1,127 @@
|
||||
<script lang="ts">
|
||||
import * as ToggleGroup from '$lib/components/ui/toggle-group';
|
||||
import Tooltip from '$lib/components/Tooltip.svelte';
|
||||
import { Separator } from '$lib/components/ui/separator';
|
||||
|
||||
import Chart from 'chart.js/auto';
|
||||
|
||||
import { selectedFiles } from '$lib/stores';
|
||||
|
||||
import { onMount } from 'svelte';
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
import {
|
||||
BrickWall,
|
||||
TriangleRight,
|
||||
HeartPulse,
|
||||
Orbit,
|
||||
SquareActivity,
|
||||
Thermometer,
|
||||
Zap
|
||||
} from 'lucide-svelte';
|
||||
|
||||
let canvas: HTMLCanvasElement;
|
||||
let chart: Chart;
|
||||
|
||||
Chart.defaults.font.family =
|
||||
'ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"'; // Tailwind CSS font
|
||||
|
||||
let elevationFill: string;
|
||||
let additionalDatasets: string[];
|
||||
|
||||
let options = {
|
||||
animation: false,
|
||||
parsing: false,
|
||||
maintainAspectRatio: false,
|
||||
scales: {
|
||||
x: {
|
||||
type: 'linear',
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Distance (km)',
|
||||
padding: 0
|
||||
}
|
||||
},
|
||||
y: {
|
||||
type: 'linear',
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Elevation (m)',
|
||||
padding: 0
|
||||
}
|
||||
}
|
||||
},
|
||||
datasets: {
|
||||
line: {
|
||||
pointRadius: 0,
|
||||
tension: 0.4
|
||||
}
|
||||
},
|
||||
interaction: {
|
||||
mode: 'nearest',
|
||||
axis: 'x',
|
||||
intersect: false
|
||||
},
|
||||
plugins: {
|
||||
legend: {
|
||||
display: false
|
||||
},
|
||||
decimation: {
|
||||
enabled: true
|
||||
}
|
||||
},
|
||||
stacked: false
|
||||
};
|
||||
|
||||
let datasets = {
|
||||
speed: {
|
||||
id: 'speed',
|
||||
label: 'Speed',
|
||||
units: 'km/h'
|
||||
},
|
||||
hr: {
|
||||
id: 'hr',
|
||||
label: 'Heart Rate',
|
||||
units: 'bpm'
|
||||
},
|
||||
cad: {
|
||||
id: 'cad',
|
||||
label: 'Cadence',
|
||||
units: 'rpm'
|
||||
},
|
||||
atemp: {
|
||||
id: 'atemp',
|
||||
label: 'Temperature',
|
||||
units: '°C'
|
||||
},
|
||||
power: {
|
||||
id: 'power',
|
||||
label: 'Power',
|
||||
units: 'W'
|
||||
}
|
||||
};
|
||||
|
||||
for (let [id, dataset] of Object.entries(datasets)) {
|
||||
options.scales[`y${id}`] = {
|
||||
type: 'linear',
|
||||
position: 'right',
|
||||
title: {
|
||||
display: true,
|
||||
text: dataset.label + ' (' + dataset.units + ')',
|
||||
padding: 0
|
||||
},
|
||||
grid: {
|
||||
display: false
|
||||
},
|
||||
display: false
|
||||
};
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
chart = new Chart(canvas, {
|
||||
type: 'line',
|
||||
data: {
|
||||
datasets: []
|
||||
},
|
||||
options: {
|
||||
animation: false,
|
||||
parsing: false,
|
||||
maintainAspectRatio: false,
|
||||
scales: {
|
||||
x: {
|
||||
type: 'linear',
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Distance (km)',
|
||||
padding: 0
|
||||
}
|
||||
},
|
||||
y: {
|
||||
type: 'linear',
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Elevation (m)',
|
||||
padding: 0
|
||||
}
|
||||
},
|
||||
y1: {
|
||||
type: 'linear',
|
||||
position: 'right',
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Speed (km/h)',
|
||||
padding: 0
|
||||
},
|
||||
grid: {
|
||||
display: false
|
||||
}
|
||||
},
|
||||
y2: {
|
||||
type: 'linear',
|
||||
position: 'right',
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Slope (%)',
|
||||
padding: 0
|
||||
},
|
||||
grid: {
|
||||
display: false
|
||||
}
|
||||
}
|
||||
},
|
||||
datasets: {
|
||||
line: {
|
||||
pointRadius: 0,
|
||||
tension: 0.4
|
||||
}
|
||||
},
|
||||
interaction: {
|
||||
mode: 'nearest',
|
||||
axis: 'x',
|
||||
intersect: false
|
||||
},
|
||||
plugins: {
|
||||
legend: {
|
||||
display: false
|
||||
},
|
||||
decimation: {
|
||||
enabled: true
|
||||
}
|
||||
},
|
||||
stacked: false
|
||||
}
|
||||
options
|
||||
});
|
||||
});
|
||||
|
||||
@@ -102,7 +141,7 @@
|
||||
fill: true
|
||||
};
|
||||
chart.data.datasets[1] = {
|
||||
label: 'Speed',
|
||||
label: datasets.speed.label,
|
||||
data: trackPointsAndStatistics.points.map((point, index) => {
|
||||
return {
|
||||
x: trackPointsAndStatistics.statistics.distance[index],
|
||||
@@ -110,18 +149,56 @@
|
||||
};
|
||||
}),
|
||||
normalized: true,
|
||||
yAxisID: 'y1'
|
||||
yAxisID: `y${datasets.speed.id}`,
|
||||
hidden: true
|
||||
};
|
||||
chart.data.datasets[2] = {
|
||||
label: 'Slope',
|
||||
label: datasets.hr.label,
|
||||
data: trackPointsAndStatistics.points.map((point, index) => {
|
||||
return {
|
||||
x: trackPointsAndStatistics.statistics.distance[index],
|
||||
y: trackPointsAndStatistics.statistics.slope[index]
|
||||
y: point.getHeartRate()
|
||||
};
|
||||
}),
|
||||
normalized: true,
|
||||
yAxisID: 'y2'
|
||||
yAxisID: `y${datasets.hr.id}`,
|
||||
hidden: true
|
||||
};
|
||||
chart.data.datasets[3] = {
|
||||
label: datasets.cad.label,
|
||||
data: trackPointsAndStatistics.points.map((point, index) => {
|
||||
return {
|
||||
x: trackPointsAndStatistics.statistics.distance[index],
|
||||
y: point.getCadence()
|
||||
};
|
||||
}),
|
||||
normalized: true,
|
||||
yAxisID: `y${datasets.cad.id}`,
|
||||
hidden: true
|
||||
};
|
||||
chart.data.datasets[4] = {
|
||||
label: datasets.atemp.label,
|
||||
data: trackPointsAndStatistics.points.map((point, index) => {
|
||||
return {
|
||||
x: trackPointsAndStatistics.statistics.distance[index],
|
||||
y: point.getTemperature()
|
||||
};
|
||||
}),
|
||||
normalized: true,
|
||||
yAxisID: `y${datasets.atemp.id}`,
|
||||
hidden: true
|
||||
};
|
||||
chart.data.datasets[5] = {
|
||||
label: datasets.power.label,
|
||||
data: trackPointsAndStatistics.points.map((point, index) => {
|
||||
return {
|
||||
x: trackPointsAndStatistics.statistics.distance[index],
|
||||
y: point.getPower()
|
||||
};
|
||||
}),
|
||||
normalized: true,
|
||||
yAxisID: `y${datasets.power.id}`,
|
||||
hidden: true
|
||||
};
|
||||
chart.options.scales.x['min'] = 0;
|
||||
chart.options.scales.x['max'] = file.statistics.distance.total;
|
||||
@@ -129,8 +206,87 @@
|
||||
chart.update();
|
||||
}
|
||||
}
|
||||
|
||||
$: console.log(elevationFill);
|
||||
$: if (additionalDatasets && chart) {
|
||||
let includeSpeed = additionalDatasets.includes('speed');
|
||||
let includeHeartRate = additionalDatasets.includes('hr');
|
||||
let includeCadence = additionalDatasets.includes('cad');
|
||||
let includeTemperature = additionalDatasets.includes('atemp');
|
||||
let includePower = additionalDatasets.includes('power');
|
||||
if (chart.data.datasets.length > 0) {
|
||||
chart.data.datasets[1].hidden = !includeSpeed;
|
||||
chart.data.datasets[2].hidden = !includeHeartRate;
|
||||
chart.data.datasets[3].hidden = !includeCadence;
|
||||
chart.data.datasets[4].hidden = !includeTemperature;
|
||||
chart.data.datasets[5].hidden = !includePower;
|
||||
}
|
||||
chart.options.scales[`y${datasets.speed.id}`].display = includeSpeed;
|
||||
chart.options.scales[`y${datasets.hr.id}`].display = includeHeartRate;
|
||||
chart.options.scales[`y${datasets.cad.id}`].display = includeCadence;
|
||||
chart.options.scales[`y${datasets.atemp.id}`].display = includeTemperature;
|
||||
chart.options.scales[`y${datasets.power.id}`].display = includePower;
|
||||
chart.update();
|
||||
}
|
||||
|
||||
onDestroy(() => {
|
||||
if (chart) {
|
||||
chart.destroy();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="h-full grow min-w-0 py-4">
|
||||
<canvas bind:this={canvas}> </canvas>
|
||||
<div class="h-full grow min-w-0 flex flex-row items-center">
|
||||
<div class="h-full grow min-w-0 py-4">
|
||||
<canvas bind:this={canvas}> </canvas>
|
||||
</div>
|
||||
<div class="h-fit flex flex-col m-2 border rounded">
|
||||
<ToggleGroup.Root class="flex-col gap-0" type="single" bind:value={elevationFill}>
|
||||
<ToggleGroup.Item class="p-0 w-8 h-8" value="slope">
|
||||
<Tooltip side="left">
|
||||
<TriangleRight slot="data" size="16" />
|
||||
<span slot="tooltip">Show slope</span>
|
||||
</Tooltip>
|
||||
</ToggleGroup.Item>
|
||||
<ToggleGroup.Item class="p-0 w-8 h-8" value="surface">
|
||||
<Tooltip side="left">
|
||||
<BrickWall slot="data" size="16" />
|
||||
<span slot="tooltip">Show surface</span>
|
||||
</Tooltip>
|
||||
</ToggleGroup.Item>
|
||||
</ToggleGroup.Root>
|
||||
<Separator />
|
||||
<ToggleGroup.Root class="flex-col gap-0" type="multiple" bind:value={additionalDatasets}>
|
||||
<ToggleGroup.Item class="p-0 w-8 h-8" value="speed">
|
||||
<Tooltip side="left">
|
||||
<Zap slot="data" size="16" />
|
||||
<span slot="tooltip">Show speed</span>
|
||||
</Tooltip>
|
||||
</ToggleGroup.Item>
|
||||
<ToggleGroup.Item class="p-0 w-8 h-8" value="hr">
|
||||
<Tooltip side="left">
|
||||
<HeartPulse slot="data" size="16" />
|
||||
<span slot="tooltip">Show heart rate</span>
|
||||
</Tooltip>
|
||||
</ToggleGroup.Item>
|
||||
<ToggleGroup.Item class="p-0 w-8 h-8" value="cad">
|
||||
<Tooltip side="left">
|
||||
<Orbit slot="data" size="16" />
|
||||
<span slot="tooltip">Show cadence</span>
|
||||
</Tooltip>
|
||||
</ToggleGroup.Item>
|
||||
<ToggleGroup.Item class="p-0 w-8 h-8" value="atemp">
|
||||
<Tooltip side="left">
|
||||
<Thermometer slot="data" size="16" />
|
||||
<span slot="tooltip">Show temperature</span>
|
||||
</Tooltip>
|
||||
</ToggleGroup.Item>
|
||||
<ToggleGroup.Item class="p-0 w-8 h-8" value="power">
|
||||
<Tooltip side="left">
|
||||
<SquareActivity slot="data" size="16" />
|
||||
<span slot="tooltip">Show power</span>
|
||||
</Tooltip>
|
||||
</ToggleGroup.Item>
|
||||
</ToggleGroup.Root>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import * as Card from '$lib/components/ui/card';
|
||||
import GPXDataItem from '$lib/components/GPXDataItem.svelte';
|
||||
import Tooltip from '$lib/components/Tooltip.svelte';
|
||||
|
||||
import { GPXStatistics } from 'gpx';
|
||||
|
||||
@@ -30,14 +30,14 @@
|
||||
|
||||
<Card.Root class="h-full overflow-hidden border-none">
|
||||
<Card.Content class="h-full flex flex-col flex-wrap gap-4 justify-center">
|
||||
<GPXDataItem>
|
||||
<Tooltip>
|
||||
<span slot="data" class="flex flex-row items-center">
|
||||
<Ruler size="18" class="mr-1" />
|
||||
{gpxData.distance.total.toFixed(2)} km
|
||||
</span>
|
||||
<span slot="tooltip">Distance</span>
|
||||
</GPXDataItem>
|
||||
<GPXDataItem>
|
||||
</Tooltip>
|
||||
<Tooltip>
|
||||
<span slot="data" class="flex flex-row items-center">
|
||||
<MoveUpRight size="18" class="mr-1" />
|
||||
{gpxData.elevation.gain.toFixed(0)} m
|
||||
@@ -45,20 +45,20 @@
|
||||
{gpxData.elevation.loss.toFixed(0)} m
|
||||
</span>
|
||||
<span slot="tooltip">Elevation</span>
|
||||
</GPXDataItem>
|
||||
<GPXDataItem>
|
||||
</Tooltip>
|
||||
<Tooltip>
|
||||
<span slot="data" class="flex flex-row items-center">
|
||||
<Zap size="18" class="mr-1" />
|
||||
{gpxData.speed.moving.toFixed(2)} km/h
|
||||
</span>
|
||||
<span slot="tooltip">Time</span>
|
||||
</GPXDataItem>
|
||||
<GPXDataItem>
|
||||
</Tooltip>
|
||||
<Tooltip>
|
||||
<span slot="data" class="flex flex-row items-center">
|
||||
<Timer size="18" class="mr-1" />
|
||||
{toHHMMSS(gpxData.time.moving)} / {toHHMMSS(gpxData.time.total)}
|
||||
</span>
|
||||
<span slot="tooltip">Moving time / Total time</span>
|
||||
</GPXDataItem>
|
||||
</Tooltip>
|
||||
</Card.Content>
|
||||
</Card.Root>
|
||||
|
@@ -11,8 +11,6 @@
|
||||
Trash2,
|
||||
HeartHandshake,
|
||||
Upload,
|
||||
CloudDownload,
|
||||
CloudUpload,
|
||||
Cloud
|
||||
} from 'lucide-svelte';
|
||||
|
||||
|
@@ -1,12 +1,14 @@
|
||||
<script lang="ts">
|
||||
import * as Tooltip from '$lib/components/ui/tooltip/index.js';
|
||||
|
||||
export let side: 'top' | 'right' | 'bottom' | 'left' = 'top';
|
||||
</script>
|
||||
|
||||
<Tooltip.Root>
|
||||
<Tooltip.Trigger>
|
||||
<slot name="data" />
|
||||
</Tooltip.Trigger>
|
||||
<Tooltip.Content side="top">
|
||||
<Tooltip.Content {side}>
|
||||
<slot name="tooltip" />
|
||||
</Tooltip.Content>
|
||||
</Tooltip.Root>
|
23
website/src/lib/components/ui/toggle-group/index.ts
Normal file
23
website/src/lib/components/ui/toggle-group/index.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import type { VariantProps } from "tailwind-variants";
|
||||
import { getContext, setContext } from "svelte";
|
||||
import Root from "./toggle-group.svelte";
|
||||
import Item from "./toggle-group-item.svelte";
|
||||
import type { toggleVariants } from "$lib/components/ui/toggle/index.js";
|
||||
|
||||
export type ToggleVariants = VariantProps<typeof toggleVariants>;
|
||||
|
||||
export function setToggleGroupCtx(props: ToggleVariants) {
|
||||
setContext("toggleGroup", props);
|
||||
}
|
||||
|
||||
export function getToggleGroupCtx() {
|
||||
return getContext<ToggleVariants>("toggleGroup");
|
||||
}
|
||||
|
||||
export {
|
||||
Root,
|
||||
Item,
|
||||
//
|
||||
Root as ToggleGroup,
|
||||
Item as ToggleGroupItem,
|
||||
};
|
@@ -0,0 +1,31 @@
|
||||
<script lang="ts">
|
||||
import { ToggleGroup as ToggleGroupPrimitive } from "bits-ui";
|
||||
import { type ToggleVariants, getToggleGroupCtx } from "./index.js";
|
||||
import { cn } from "$lib/utils.js";
|
||||
import { toggleVariants } from "$lib/components/ui/toggle/index.js";
|
||||
|
||||
type $$Props = ToggleGroupPrimitive.ItemProps & ToggleVariants;
|
||||
|
||||
let className: string | undefined | null = undefined;
|
||||
|
||||
export { className as class };
|
||||
export let variant: $$Props["variant"] = "default";
|
||||
export let size: $$Props["size"] = "default";
|
||||
export let value: $$Props["value"];
|
||||
|
||||
const ctx = getToggleGroupCtx();
|
||||
</script>
|
||||
|
||||
<ToggleGroupPrimitive.Item
|
||||
class={cn(
|
||||
toggleVariants({
|
||||
variant: ctx.variant || variant,
|
||||
size: ctx.size || size,
|
||||
}),
|
||||
className
|
||||
)}
|
||||
{value}
|
||||
{...$$restProps}
|
||||
>
|
||||
<slot />
|
||||
</ToggleGroupPrimitive.Item>
|
@@ -0,0 +1,30 @@
|
||||
<script lang="ts">
|
||||
import type { VariantProps } from "tailwind-variants";
|
||||
import { ToggleGroup as ToggleGroupPrimitive } from "bits-ui";
|
||||
import { setToggleGroupCtx } from "./index.js";
|
||||
import type { toggleVariants } from "$lib/components/ui/toggle/index.js";
|
||||
import { cn } from "$lib/utils.js";
|
||||
|
||||
type T = $$Generic<"single" | "multiple">;
|
||||
type $$Props = ToggleGroupPrimitive.Props<T> & VariantProps<typeof toggleVariants>;
|
||||
|
||||
let className: string | undefined | null = undefined;
|
||||
export { className as class };
|
||||
export let variant: $$Props["variant"] = "default";
|
||||
export let size: $$Props["size"] = "default";
|
||||
export let value: $$Props["value"] = undefined;
|
||||
|
||||
setToggleGroupCtx({
|
||||
variant,
|
||||
size,
|
||||
});
|
||||
</script>
|
||||
|
||||
<ToggleGroupPrimitive.Root
|
||||
class={cn("flex items-center justify-center gap-1", className)}
|
||||
bind:value
|
||||
{...$$restProps}
|
||||
let:builder
|
||||
>
|
||||
<slot {builder} />
|
||||
</ToggleGroupPrimitive.Root>
|
31
website/src/lib/components/ui/toggle/index.ts
Normal file
31
website/src/lib/components/ui/toggle/index.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { type VariantProps, tv } from "tailwind-variants";
|
||||
import Root from "./toggle.svelte";
|
||||
|
||||
export const toggleVariants = tv({
|
||||
base: "inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors hover:bg-muted hover:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground",
|
||||
variants: {
|
||||
variant: {
|
||||
default: "bg-transparent",
|
||||
outline:
|
||||
"border border-input bg-transparent hover:bg-accent hover:text-accent-foreground",
|
||||
},
|
||||
size: {
|
||||
default: "h-10 px-3",
|
||||
sm: "h-9 px-2.5",
|
||||
lg: "h-11 px-5",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
size: "default",
|
||||
},
|
||||
});
|
||||
|
||||
export type Variant = VariantProps<typeof toggleVariants>["variant"];
|
||||
export type Size = VariantProps<typeof toggleVariants>["size"];
|
||||
|
||||
export {
|
||||
Root,
|
||||
//
|
||||
Root as Toggle,
|
||||
};
|
26
website/src/lib/components/ui/toggle/toggle.svelte
Normal file
26
website/src/lib/components/ui/toggle/toggle.svelte
Normal file
@@ -0,0 +1,26 @@
|
||||
<script lang="ts">
|
||||
import { Toggle as TogglePrimitive } from "bits-ui";
|
||||
import { type Size, type Variant, toggleVariants } from "./index.js";
|
||||
import { cn } from "$lib/utils.js";
|
||||
|
||||
type $$Props = TogglePrimitive.Props & {
|
||||
variant?: Variant;
|
||||
size?: Size;
|
||||
};
|
||||
type $$Events = TogglePrimitive.Events;
|
||||
|
||||
let className: $$Props["class"] = undefined;
|
||||
export let variant: $$Props["variant"] = "default";
|
||||
export let size: $$Props["size"] = "default";
|
||||
export let pressed: $$Props["pressed"] = undefined;
|
||||
export { className as class };
|
||||
</script>
|
||||
|
||||
<TogglePrimitive.Root
|
||||
bind:pressed
|
||||
class={cn(toggleVariants({ variant, size, className }))}
|
||||
{...$$restProps}
|
||||
on:click
|
||||
>
|
||||
<slot />
|
||||
</TogglePrimitive.Root>
|
Reference in New Issue
Block a user