mirror of
				https://github.com/gpxstudio/gpx.studio.git
				synced 2025-11-04 05:21:09 +00:00 
			
		
		
		
	beginning of map layer control
This commit is contained in:
		
							
								
								
									
										24
									
								
								website/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										24
									
								
								website/package-lock.json
									
									
									
										generated
									
									
									
								
							@@ -11,6 +11,8 @@
 | 
			
		||||
                "@fortawesome/free-brands-svg-icons": "^6.5.2",
 | 
			
		||||
                "@fortawesome/free-solid-svg-icons": "^6.5.2",
 | 
			
		||||
                "@mapbox/mapbox-gl-geocoder": "^5.0.2",
 | 
			
		||||
                "@types/mapbox__mapbox-gl-geocoder": "^5.0.0",
 | 
			
		||||
                "@types/mapbox-gl": "^3.1.0",
 | 
			
		||||
                "bits-ui": "^0.21.2",
 | 
			
		||||
                "clsx": "^2.1.0",
 | 
			
		||||
                "gpx": "file:../gpx",
 | 
			
		||||
@@ -1370,6 +1372,11 @@
 | 
			
		||||
            "integrity": "sha512-trOc4AAUThEz9hapPtSd7wf5tiQKvTtu5b371UxXdTuqzIh0ArcRspRP0i0Viu+LXstIQ1z96t1nsPxT9ol01g==",
 | 
			
		||||
            "dev": true
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/@types/geojson": {
 | 
			
		||||
            "version": "7946.0.14",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.14.tgz",
 | 
			
		||||
            "integrity": "sha512-WCfD5Ht3ZesJUsONdhvm84dmzWOiOzOAqOncN0++w0lBw1o8OuDNJF2McvvCef/yBqb/HYRahp1BYtODFQ8bRg=="
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/@types/http-cache-semantics": {
 | 
			
		||||
            "version": "4.0.4",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz",
 | 
			
		||||
@@ -1389,6 +1396,23 @@
 | 
			
		||||
                "@types/node": "*"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/@types/mapbox__mapbox-gl-geocoder": {
 | 
			
		||||
            "version": "5.0.0",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@types/mapbox__mapbox-gl-geocoder/-/mapbox__mapbox-gl-geocoder-5.0.0.tgz",
 | 
			
		||||
            "integrity": "sha512-eGBWdFiP2QgmwndPyhwK6eBeOfyB8vRscp2C6Acqasx5dH8FvTo/VgXWCrCKFR3zkWek/H4w4/CwmBFOs7OLBA==",
 | 
			
		||||
            "dependencies": {
 | 
			
		||||
                "@types/geojson": "*",
 | 
			
		||||
                "@types/mapbox-gl": "*"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/@types/mapbox-gl": {
 | 
			
		||||
            "version": "3.1.0",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@types/mapbox-gl/-/mapbox-gl-3.1.0.tgz",
 | 
			
		||||
            "integrity": "sha512-hI6cQDjw1bkJw7MC/eHMqq5TWUamLwsujnUUeiIX2KDRjxRNSYMjnHz07+LATz9I9XIsKumOtUz4gRYnZOJ/FA==",
 | 
			
		||||
            "dependencies": {
 | 
			
		||||
                "@types/geojson": "*"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "node_modules/@types/minimist": {
 | 
			
		||||
            "version": "1.2.5",
 | 
			
		||||
            "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.5.tgz",
 | 
			
		||||
 
 | 
			
		||||
@@ -40,6 +40,8 @@
 | 
			
		||||
        "@fortawesome/free-brands-svg-icons": "^6.5.2",
 | 
			
		||||
        "@fortawesome/free-solid-svg-icons": "^6.5.2",
 | 
			
		||||
        "@mapbox/mapbox-gl-geocoder": "^5.0.2",
 | 
			
		||||
        "@types/mapbox__mapbox-gl-geocoder": "^5.0.0",
 | 
			
		||||
        "@types/mapbox-gl": "^3.1.0",
 | 
			
		||||
        "bits-ui": "^0.21.2",
 | 
			
		||||
        "clsx": "^2.1.0",
 | 
			
		||||
        "gpx": "file:../gpx",
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										85
									
								
								website/src/lib/assets/layers.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								website/src/lib/assets/layers.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,85 @@
 | 
			
		||||
import { type AnySourceData, type Style } from 'mapbox-gl';
 | 
			
		||||
 | 
			
		||||
export const basemaps: { [key: string]: string | Style; } = {
 | 
			
		||||
    mapboxOutdoors: 'mapbox://styles/mapbox/outdoors-v12',
 | 
			
		||||
    mapboxSatellite: 'mapbox://styles/mapbox/satellite-v9',
 | 
			
		||||
    openStreetMap: {
 | 
			
		||||
        version: 8,
 | 
			
		||||
        sources: {
 | 
			
		||||
            openStreetMap: {
 | 
			
		||||
                type: 'raster',
 | 
			
		||||
                tiles: ['https://a.tile.openstreetmap.org/{z}/{x}/{y}.png', 'https://b.tile.openstreetmap.org/{z}/{x}/{y}.png', 'https://c.tile.openstreetmap.org/{z}/{x}/{y}.png'],
 | 
			
		||||
                tileSize: 256,
 | 
			
		||||
                maxzoom: 18,
 | 
			
		||||
                attribution: 'Map tiles by <a target="_top" rel="noopener" href="https://tile.openstreetmap.org/">OpenStreetMap tile servers</a>, under the <a target="_top" rel="noopener" href="https://operations.osmfoundation.org/policies/tiles/">tile usage policy</a>. Data by <a target="_top" rel="noopener" href="http://openstreetmap.org">OpenStreetMap</a>'
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        layers: [{
 | 
			
		||||
            id: 'openStreetMap',
 | 
			
		||||
            type: 'raster',
 | 
			
		||||
            source: 'openStreetMap',
 | 
			
		||||
        }],
 | 
			
		||||
    },
 | 
			
		||||
    openTopoMap: {
 | 
			
		||||
        version: 8,
 | 
			
		||||
        sources: {
 | 
			
		||||
            openTopoMap: {
 | 
			
		||||
                type: 'raster',
 | 
			
		||||
                tiles: ['https://tile.opentopomap.org/{z}/{x}/{y}.png'],
 | 
			
		||||
                tileSize: 256,
 | 
			
		||||
                maxzoom: 17,
 | 
			
		||||
                attribution: '© <a href="https://www.opentopomap.org" target="_blank">OpenTopoMap</a> © <a href="https://www.openstreetmap.org/copyright" target="_blank">OpenStreetMap</a>'
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        layers: [{
 | 
			
		||||
            id: 'openTopoMap',
 | 
			
		||||
            type: 'raster',
 | 
			
		||||
            source: 'openTopoMap',
 | 
			
		||||
        }],
 | 
			
		||||
    },
 | 
			
		||||
    openHikingMap: {
 | 
			
		||||
        version: 8,
 | 
			
		||||
        sources: {
 | 
			
		||||
            openHikingMap: {
 | 
			
		||||
                type: 'raster',
 | 
			
		||||
                tiles: ['https://maps.refuges.info/hiking/{z}/{x}/{y}.png'],
 | 
			
		||||
                tileSize: 256,
 | 
			
		||||
                maxzoom: 18,
 | 
			
		||||
                attribution: '© <a href="https://wiki.openstreetmap.org/wiki/Hiking/mri" target="_blank">sly</a> © <a href="https://www.openstreetmap.org/copyright" target="_blank">OpenStreetMap</a>'
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        layers: [{
 | 
			
		||||
            id: 'openHikingMap',
 | 
			
		||||
            type: 'raster',
 | 
			
		||||
            source: 'openHikingMap',
 | 
			
		||||
        }],
 | 
			
		||||
    },
 | 
			
		||||
    cyclOSM: {
 | 
			
		||||
        version: 8,
 | 
			
		||||
        sources: {
 | 
			
		||||
            cyclOSM: {
 | 
			
		||||
                type: 'raster',
 | 
			
		||||
                tiles: ['https://a.tile-cyclosm.openstreetmap.fr/cyclosm/{z}/{x}/{y}.png', 'https://b.tile-cyclosm.openstreetmap.fr/cyclosm/{z}/{x}/{y}.png', 'https://c.tile-cyclosm.openstreetmap.fr/cyclosm/{z}/{x}/{y}.png'],
 | 
			
		||||
                tileSize: 256,
 | 
			
		||||
                maxzoom: 17,
 | 
			
		||||
                attribution: '© <a href="https://github.com/cyclosm/cyclosm-cartocss-style/releases" title="CyclOSM - Open Bicycle render">CyclOSM</a> © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>'
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        layers: [{
 | 
			
		||||
            id: 'cyclOSM',
 | 
			
		||||
            type: 'raster',
 | 
			
		||||
            source: 'cyclOSM',
 | 
			
		||||
        }],
 | 
			
		||||
    },
 | 
			
		||||
    linz: 'https://basemaps.linz.govt.nz/v1/tiles/topographic/EPSG:3857/style/topographic.json?api=d01fbtg0ar23gctac5m0jgyy2ds'
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const overlays: { [key: string]: AnySourceData; } = {
 | 
			
		||||
    cyclOSMlite: {
 | 
			
		||||
        type: 'raster',
 | 
			
		||||
        tiles: ['https://a.tile-cyclosm.openstreetmap.fr/cyclosm-lite/{z}/{x}/{y}.png', 'https://b.tile-cyclosm.openstreetmap.fr/cyclosm-lite/{z}/{x}/{y}.png', 'https://c.tile-cyclosm.openstreetmap.fr/cyclosm-lite/{z}/{x}/{y}.png'],
 | 
			
		||||
        tileSize: 256,
 | 
			
		||||
        maxzoom: 17,
 | 
			
		||||
        attribution: '© <a href="https://github.com/cyclosm/cyclosm-cartocss-style/releases" title="CyclOSM - Open Bicycle render">CyclOSM</a> © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>'
 | 
			
		||||
    },
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										63
									
								
								website/src/lib/components/LayerControl.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								website/src/lib/components/LayerControl.svelte
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,63 @@
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
	import mapboxgl from 'mapbox-gl';
 | 
			
		||||
	import CustomControl from '$lib/components/custom-control/CustomControl.svelte';
 | 
			
		||||
 | 
			
		||||
	import Fa from 'svelte-fa';
 | 
			
		||||
	import { faLayerGroup } from '@fortawesome/free-solid-svg-icons';
 | 
			
		||||
 | 
			
		||||
	import { Label } from '$lib/components/ui/label';
 | 
			
		||||
	import * as RadioGroup from '$lib/components/ui/radio-group';
 | 
			
		||||
	import { Checkbox } from '$lib/components/ui/checkbox';
 | 
			
		||||
 | 
			
		||||
	import { basemaps, overlays } from '$lib/assets/layers';
 | 
			
		||||
 | 
			
		||||
	export let map: mapboxgl.Map | null;
 | 
			
		||||
 | 
			
		||||
	$: if (map) {
 | 
			
		||||
		map?.setStyle(basemaps['mapboxOutdoors']);
 | 
			
		||||
	}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<CustomControl {map} class="group">
 | 
			
		||||
	<div class="flex flex-row justify-center items-center w-[29px] h-[29px] group-hover:hidden">
 | 
			
		||||
		<Fa icon={faLayerGroup} size="1.4x" />
 | 
			
		||||
	</div>
 | 
			
		||||
	<div class="hidden group-hover:block p-2">
 | 
			
		||||
		<RadioGroup.Root
 | 
			
		||||
			value="mapboxOutdoors"
 | 
			
		||||
			onValueChange={(id) => {
 | 
			
		||||
				map.setStyle(basemaps[id]);
 | 
			
		||||
			}}
 | 
			
		||||
		>
 | 
			
		||||
			{#each Object.keys(basemaps) as id}
 | 
			
		||||
				<div class="flex items-center space-x-2">
 | 
			
		||||
					<RadioGroup.Item value={id} {id} />
 | 
			
		||||
					<Label for={id}>{id}</Label>
 | 
			
		||||
				</div>
 | 
			
		||||
			{/each}
 | 
			
		||||
		</RadioGroup.Root>
 | 
			
		||||
		<div>
 | 
			
		||||
			{#each Object.keys(overlays) as id}
 | 
			
		||||
				<Checkbox
 | 
			
		||||
					{id}
 | 
			
		||||
					onCheckedChange={(checked) => {
 | 
			
		||||
						console.log('onCheckedChange', map?.isStyleLoaded());
 | 
			
		||||
						if (checked) {
 | 
			
		||||
							if (!map.getSource(id)) {
 | 
			
		||||
								map.addSource(id, overlays[id]);
 | 
			
		||||
							}
 | 
			
		||||
							map.addLayer({
 | 
			
		||||
								id,
 | 
			
		||||
								type: overlays[id].type === 'raster' ? 'raster' : 'line',
 | 
			
		||||
								source: id
 | 
			
		||||
							});
 | 
			
		||||
						} else {
 | 
			
		||||
							map.removeLayer(id);
 | 
			
		||||
						}
 | 
			
		||||
					}}
 | 
			
		||||
				/>
 | 
			
		||||
				<Label for={id}>{id}</Label>
 | 
			
		||||
			{/each}
 | 
			
		||||
		</div>
 | 
			
		||||
	</div>
 | 
			
		||||
</CustomControl>
 | 
			
		||||
@@ -6,6 +6,7 @@
 | 
			
		||||
 | 
			
		||||
	import MapboxGeocoder from '@mapbox/mapbox-gl-geocoder';
 | 
			
		||||
	import '@mapbox/mapbox-gl-geocoder/dist/mapbox-gl-geocoder.css';
 | 
			
		||||
	import LayerControl from './LayerControl.svelte';
 | 
			
		||||
 | 
			
		||||
	mapboxgl.accessToken =
 | 
			
		||||
		'pk.eyJ1IjoiZ3B4c3R1ZGlvIiwiYSI6ImNrdHVoM2pjNTBodmUycG1yZTNwcnJ3MzkifQ.YZnNs9s9oCQPzoXAWs_SLg';
 | 
			
		||||
@@ -17,7 +18,7 @@
 | 
			
		||||
	onMount(() => {
 | 
			
		||||
		map = new mapboxgl.Map({
 | 
			
		||||
			container: 'map',
 | 
			
		||||
			style: 'mapbox://styles/mapbox/outdoors-v12',
 | 
			
		||||
			style: { version: 8, sources: {}, layers: [] },
 | 
			
		||||
			projection: 'mercator',
 | 
			
		||||
			hash: true,
 | 
			
		||||
			language: 'auto',
 | 
			
		||||
@@ -57,10 +58,18 @@
 | 
			
		||||
			})
 | 
			
		||||
		);
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	onDestroy(() => {
 | 
			
		||||
		if (map) {
 | 
			
		||||
			map.remove();
 | 
			
		||||
		}
 | 
			
		||||
	});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<div {...$$restProps}>
 | 
			
		||||
	<div id="map" class="h-full"></div>
 | 
			
		||||
	<div id="map" class="h-full">
 | 
			
		||||
		<LayerControl {map} />
 | 
			
		||||
	</div>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<style lang="postcss">
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,25 @@
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
	import CustomControl from './CustomControl';
 | 
			
		||||
	import mapboxgl from 'mapbox-gl';
 | 
			
		||||
 | 
			
		||||
	export let map: mapboxgl.Map | null;
 | 
			
		||||
	export let position: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' = 'top-right';
 | 
			
		||||
 | 
			
		||||
	let container: HTMLDivElement | null = null;
 | 
			
		||||
 | 
			
		||||
	$: if (map && container) {
 | 
			
		||||
		if (position.includes('right')) container.classList.add('float-right');
 | 
			
		||||
		else container.classList.add('float-left');
 | 
			
		||||
		container.classList.remove('hidden');
 | 
			
		||||
		let control = new CustomControl(container);
 | 
			
		||||
		map.addControl(control, position);
 | 
			
		||||
	}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<div
 | 
			
		||||
	bind:this={container}
 | 
			
		||||
	class="{$$props.class ||
 | 
			
		||||
		''} clear-both translate-0 m-[10px] pointer-events-auto bg-background rounded shadow-md hidden"
 | 
			
		||||
>
 | 
			
		||||
	<slot />
 | 
			
		||||
</div>
 | 
			
		||||
							
								
								
									
										20
									
								
								website/src/lib/components/custom-control/CustomControl.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								website/src/lib/components/custom-control/CustomControl.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
			
		||||
import { type Map, type IControl } from 'mapbox-gl';
 | 
			
		||||
 | 
			
		||||
export default class CustomControl implements IControl {
 | 
			
		||||
    _map: Map | undefined;
 | 
			
		||||
    _container: HTMLElement;
 | 
			
		||||
 | 
			
		||||
    constructor(container: HTMLElement) {
 | 
			
		||||
        this._container = container;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    onAdd(map: Map): HTMLElement {
 | 
			
		||||
        this._map = map;
 | 
			
		||||
        return this._container;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    onRemove() {
 | 
			
		||||
        this._container?.parentNode?.removeChild(this._container);
 | 
			
		||||
        this._map = undefined;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										35
									
								
								website/src/lib/components/ui/checkbox/checkbox.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								website/src/lib/components/ui/checkbox/checkbox.svelte
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
	import { Checkbox as CheckboxPrimitive } from "bits-ui";
 | 
			
		||||
	import Check from "lucide-svelte/icons/check";
 | 
			
		||||
	import Minus from "lucide-svelte/icons/minus";
 | 
			
		||||
	import { cn } from "$lib/utils.js";
 | 
			
		||||
 | 
			
		||||
	type $$Props = CheckboxPrimitive.Props;
 | 
			
		||||
	type $$Events = CheckboxPrimitive.Events;
 | 
			
		||||
 | 
			
		||||
	let className: $$Props["class"] = undefined;
 | 
			
		||||
	export let checked: $$Props["checked"] = false;
 | 
			
		||||
	export { className as class };
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<CheckboxPrimitive.Root
 | 
			
		||||
	class={cn(
 | 
			
		||||
		"peer box-content h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[disabled=true]:cursor-not-allowed data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground data-[disabled=true]:opacity-50",
 | 
			
		||||
		className
 | 
			
		||||
	)}
 | 
			
		||||
	bind:checked
 | 
			
		||||
	{...$$restProps}
 | 
			
		||||
	on:click
 | 
			
		||||
>
 | 
			
		||||
	<CheckboxPrimitive.Indicator
 | 
			
		||||
		class={cn("flex h-4 w-4 items-center justify-center text-current")}
 | 
			
		||||
		let:isChecked
 | 
			
		||||
		let:isIndeterminate
 | 
			
		||||
	>
 | 
			
		||||
		{#if isChecked}
 | 
			
		||||
			<Check class="h-3.5 w-3.5" />
 | 
			
		||||
		{:else if isIndeterminate}
 | 
			
		||||
			<Minus class="h-3.5 w-3.5" />
 | 
			
		||||
		{/if}
 | 
			
		||||
	</CheckboxPrimitive.Indicator>
 | 
			
		||||
</CheckboxPrimitive.Root>
 | 
			
		||||
							
								
								
									
										6
									
								
								website/src/lib/components/ui/checkbox/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								website/src/lib/components/ui/checkbox/index.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
import Root from "./checkbox.svelte";
 | 
			
		||||
export {
 | 
			
		||||
	Root,
 | 
			
		||||
	//
 | 
			
		||||
	Root as Checkbox,
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										7
									
								
								website/src/lib/components/ui/label/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								website/src/lib/components/ui/label/index.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
			
		||||
import Root from "./label.svelte";
 | 
			
		||||
 | 
			
		||||
export {
 | 
			
		||||
	Root,
 | 
			
		||||
	//
 | 
			
		||||
	Root as Label,
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										21
									
								
								website/src/lib/components/ui/label/label.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								website/src/lib/components/ui/label/label.svelte
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
	import { Label as LabelPrimitive } from "bits-ui";
 | 
			
		||||
	import { cn } from "$lib/utils.js";
 | 
			
		||||
 | 
			
		||||
	type $$Props = LabelPrimitive.Props;
 | 
			
		||||
	type $$Events = LabelPrimitive.Events;
 | 
			
		||||
 | 
			
		||||
	let className: $$Props["class"] = undefined;
 | 
			
		||||
	export { className as class };
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<LabelPrimitive.Root
 | 
			
		||||
	class={cn(
 | 
			
		||||
		"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
 | 
			
		||||
		className
 | 
			
		||||
	)}
 | 
			
		||||
	{...$$restProps}
 | 
			
		||||
	on:mousedown
 | 
			
		||||
>
 | 
			
		||||
	<slot />
 | 
			
		||||
</LabelPrimitive.Root>
 | 
			
		||||
							
								
								
									
										15
									
								
								website/src/lib/components/ui/radio-group/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								website/src/lib/components/ui/radio-group/index.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
			
		||||
import { RadioGroup as RadioGroupPrimitive } from "bits-ui";
 | 
			
		||||
 | 
			
		||||
import Root from "./radio-group.svelte";
 | 
			
		||||
import Item from "./radio-group-item.svelte";
 | 
			
		||||
const Input = RadioGroupPrimitive.Input;
 | 
			
		||||
 | 
			
		||||
export {
 | 
			
		||||
	Root,
 | 
			
		||||
	Input,
 | 
			
		||||
	Item,
 | 
			
		||||
	//
 | 
			
		||||
	Root as RadioGroup,
 | 
			
		||||
	Input as RadioGroupInput,
 | 
			
		||||
	Item as RadioGroupItem,
 | 
			
		||||
};
 | 
			
		||||
@@ -0,0 +1,28 @@
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
	import { RadioGroup as RadioGroupPrimitive } from "bits-ui";
 | 
			
		||||
	import Circle from "lucide-svelte/icons/circle";
 | 
			
		||||
	import { cn } from "$lib/utils.js";
 | 
			
		||||
 | 
			
		||||
	type $$Props = RadioGroupPrimitive.ItemProps;
 | 
			
		||||
	type $$Events = RadioGroupPrimitive.ItemEvents;
 | 
			
		||||
 | 
			
		||||
	let className: $$Props["class"] = undefined;
 | 
			
		||||
	export let value: $$Props["value"];
 | 
			
		||||
	export { className as class };
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<RadioGroupPrimitive.Item
 | 
			
		||||
	{value}
 | 
			
		||||
	class={cn(
 | 
			
		||||
		"aspect-square h-4 w-4 rounded-full border border-primary text-primary ring-offset-background focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
 | 
			
		||||
		className
 | 
			
		||||
	)}
 | 
			
		||||
	{...$$restProps}
 | 
			
		||||
	on:click
 | 
			
		||||
>
 | 
			
		||||
	<div class="flex items-center justify-center">
 | 
			
		||||
		<RadioGroupPrimitive.ItemIndicator>
 | 
			
		||||
			<Circle class="h-2.5 w-2.5 fill-current text-current" />
 | 
			
		||||
		</RadioGroupPrimitive.ItemIndicator>
 | 
			
		||||
	</div>
 | 
			
		||||
</RadioGroupPrimitive.Item>
 | 
			
		||||
							
								
								
									
										14
									
								
								website/src/lib/components/ui/radio-group/radio-group.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								website/src/lib/components/ui/radio-group/radio-group.svelte
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
	import { RadioGroup as RadioGroupPrimitive } from "bits-ui";
 | 
			
		||||
	import { cn } from "$lib/utils.js";
 | 
			
		||||
 | 
			
		||||
	type $$Props = RadioGroupPrimitive.Props;
 | 
			
		||||
 | 
			
		||||
	let className: $$Props["class"] = undefined;
 | 
			
		||||
	export let value: $$Props["value"] = undefined;
 | 
			
		||||
	export { className as class };
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<RadioGroupPrimitive.Root bind:value class={cn("grid gap-2", className)} {...$$restProps}>
 | 
			
		||||
	<slot />
 | 
			
		||||
</RadioGroupPrimitive.Root>
 | 
			
		||||
		Reference in New Issue
	
	Block a user