mapcn-svelte

repository·main·Indexed 41 days ago

https://github.com/mariuslang/mapcn-svelte

A suite of declarative, theme-aware map components for Svelte 5 applications. Built on MapLibre GL and styled with Tailwind CSS, it integrates with the shadcn-svelte ecosystem to provide components for markers, popups, routes, GeoJSON layers, and choropleth maps. Version 0.3 includes native Svelte 5 rune reactivity and support for multiple tile providers.

Tokens
19.5K
Snippets
73
Records
85
Agent score
53%

What's inside mapcn-svelte

  1. Overview of mapcn-svelte

    main

    mapcn-svelte is a library of free and open-source map components for Svelte 5. It is built on top of MapLibre GL, styled with Tailwind CSS, and designed to work seamlessly with shadcn-svelte.

    Key capabilities include:

    • Theme-aware UI: Automatically adapts to light and dark modes.
    • Composable Components: Declarative components for building complex map interfaces.
    • Rich Features: Support for markers, popups, tooltips, labels, routes/paths, and standard map controls (zoom, compass, locate, fullscreen).
    • Svelte 5 Native: Built using Svelte 5 runes for optimal reactivity.
  2. Basemap Terms of Service and Tile Providers

    main

    By default, this project uses CARTO Basemaps (based on OpenStreetMap data). Users must adhere to the following usage terms:

    • Commercial use: Requires a CARTO Enterprise license.
    • Non-commercial use: Free for CARTO grantees under their specific basemap terms.

    Alternatives: You can switch to other MapLibre-compatible tile providers such as OpenStreetMap tiles, MapTiler, or Stadia Maps to avoid CARTO licensing requirements.

  3. Use the Sidebar UI component library

    main

    The mapcn-svelte sidebar package provides a comprehensive set of Svelte components for building complex sidebar layouts. The library uses a provider-based pattern where a SidebarProvider (or Sidebar) manages the state, and various sub-components like SidebarContent, SidebarHeader, SidebarFooter, and SidebarMenu are used to structure the UI.

    Key components include:

    • Layout Containers: Sidebar (Root), SidebarContent, SidebarHeader, SidebarFooter, SidebarInset, and SidebarRail.
    • Menu System: SidebarMenu, SidebarMenuItem, SidebarMenuButton, SidebarMenuBadge, and nested structures like SidebarMenuSub.
    • Grouping: SidebarGroup, SidebarGroupLabel, SidebarGroupContent, and SidebarGroupAction.
    • Controls: SidebarTrigger for toggling sidebar state and useSidebar for accessing the sidebar context.

    Note: Many components are exported with both their original names and prefixed with Sidebar (e.g., Root is exported as both Root and Sidebar) to allow for flexible importing.

    import {
      Sidebar,
      SidebarContent,
      SidebarHeader,
      SidebarFooter,
      SidebarTrigger,
      SidebarMenu,
      SidebarMenuItem,
      SidebarMenuButton
    } from "./sidebar";
    
    // Example structure
    <Sidebar>
      <SidebarHeader>Header Content</SidebarHeader>
      <SidebarContent>
        <SidebarMenu>
          <SidebarMenuItem>
            <SidebarMenuButton>Menu Item</SidebarMenuButton>
          </SidebarMenuItem>
        </SidebarMenu>
      </SidebarContent>
      <SidebarFooter>Footer Content</SidebarFooter>
      <SidebarTrigger />
    </Sidebar>
  4. Configure Choropleth map view and color scales

    main

    The mapConfig object defines the visual and spatial parameters for a choropleth map block. It includes:

    • view: Controls the initial map state with center (an array of [longitude, latitude]), zoom, and zoom constraints (minZoom, maxZoom).
    • scaleStops: An array of numeric thresholds used to map data values to the color ramp.
    • colors: A theme-based configuration supporting both light and dark modes. Each theme defines:
      • base: The fill color for countries with no data (value 0).
      • ramp: A sequential array of four color strings mapped to the scaleStops.
      • hover: The fill color applied when a country is hovered.
    export const mapConfig = {
    	view: {
    		center: [12, 28] as [number, number],
    		zoom: 1.4,
    		minZoom: 1,
    		maxZoom: 4,
    	},
    	scaleStops: [0, 25, 50, 75, 100] as const,
    	colors: {
    		light: {
    			base: "#f0f0f0",
    			ramp: ["#d4d4d4", "#a3a3a3", "#737373", "#404040"],
    			hover: "#0a0a0a",
    		},
    		dark: {
    			base: "#2a2a2a",
    			ramp: ["#404040", "#737373", "#a3a3a3", "#d4d4d4"],
    			hover: "#ffffff",
    		},
    	},
    };
  5. Import core map components and types

    main

    The src/lib/registry/blocks/map/index.ts entrypoint provides access to all primary map-related components and types in mapcn-svelte. Use these to build interactive maps with markers, routes, clusters, and GeoJSON layers.

    import {
      Map,
      MapMarker,
      MapControls,
      MapRoute,
      MapClusterLayer,
      MapGeoJSON,
      useMap,
      type MapViewport
    } from "./lib/registry/blocks/map";
  6. Use the CommandDialog component

    main

    The CommandDialog component provides a searchable command palette interface wrapped in a dialog. It combines bits-ui Dialog and Command primitives to allow users to search for and execute commands within a modal overlay.

    Key features:

    • Two-way binding: Use bind:open to control visibility and bind:value to track the current search input.
    • Accessibility: Includes a visually hidden (screen-reader only) header containing a title and description for compliance with dialog accessibility standards.
    • Customization: Accepts portalProps to control where the dialog is rendered in the DOM and restProps to pass through standard Dialog and Command primitive attributes.
    <script>
      import CommandDialog from "$lib/registry/ui/command/command-dialog.svelte";
    
      let open = $state(false);
      let value = $state("");
    </script>
    
    <CommandDialog 
      bind:open 
      bind:value 
      title="Quick Actions" 
      description="Search for commands..."
    >
      <!-- Your command items go here -->
      <div class="p-4">Command List Content</div>
    </CommandDialog>
  7. Use the Stepper UI components

    main

    The Stepper component is a custom UI component used to create step-by-step progress indicators. It is composed of a root Stepper component and individual StepperItem components. You can use the aliases Root for Stepper and Item for StepperItem for more concise component naming.

    To use them, wrap one or more StepperItem components within a Stepper component.

    <script>
      import { Stepper, StepperItem } from "$lib/registry/ui/stepper";
      // Or using aliases:
      // import { Root, Item } from "$lib/registry/ui/stepper";
    </script>
    
    <Stepper>
      <StepperItem>Step 1</StepperItem>
      <StepperItem>Step 2</StepperItem>
      <StepperItem>Step 3</StepperItem>
    </Stepper>
  8. Retrieve example source code with getExampleSource()

    main

    Use getExampleSource(name: string) to retrieve the raw source code of a specific Svelte component example located in the $lib/components/docs/preview/examples/ directory. The function automatically normalizes imports by replacing @/registry/map with @/components/ui/map to ensure the code is usable in different contexts.

    Note that the name parameter should not include the .svelte extension.

    // Example usage:
    const code = getExampleSource('my-map-example');
    console.log(code);
  9. Use the Kbd and KbdGroup components

    main

    The kbd registry provides components for displaying keyboard shortcuts in your UI. You can use Kbd (aliased from Root) as the main container and KbdGroup (aliased from Group) to organize related shortcuts. These components are designed to be used within your Svelte application to represent key combinations visually.

    <script>
      import { Kbd, KbdGroup } from "$lib/registry/ui/kbd";
    </script>
    
    <Kbd>
      <KbdGroup>
        <!-- Shortcut items go here -->
      </KbdGroup>
    </Kbd>
  10. Use the Button component

    main

    The Button component is the primary entry point for button UI elements. It is exported as Root (aliased to Button) and accepts props defined by the Props type (aliased from ButtonProps). You can also use the buttonVariants utility to apply specific styles to other elements or customize the button's appearance using the ButtonVariant and ButtonSize types.

    <script>
      import { Button, type ButtonVariant } from "$lib/registry/ui/button";
    </script>
    
    <Button variant="default" size="lg">
      Click me
    </Button>
  11. Manage sidebar state with setSidebar and useSidebar

    main

    The sidebar state is managed via a context-based system. You must initialize the sidebar state using setSidebar (typically within a Sidebar.Provider component or a parent component) and can then access that state in any child component using the useSidebar hook.

    Important: useSidebar returns a SidebarState class instance. You cannot destructure this instance; you must access its properties directly from the returned object to maintain reactivity.

    SidebarState Properties

    • open: A derived boolean indicating if the sidebar is expanded.
    • openMobile: A stateful boolean indicating if the sidebar is open on mobile devices.
    • isMobile: A boolean indicating if the current device is detected as mobile.
    • state: A derived string that is either "expanded" or "collapsed".
    • toggle(): A method to switch the sidebar state. It automatically handles the distinction between mobile (openMobile) and desktop (open) states.
    • setOpenMobile(value: boolean): Manually sets the mobile-specific open state.
    • handleShortcutKeydown: An event handler for KeyboardEvent that can be attached to <svelte:window> to support keyboard shortcuts (using SIDEBAR_KEYBOARD_SHORTCUT with metaKey or ctrlKey).
    import { setSidebar, useSidebar } from "./path-to-context";
    
    // 1. In a parent component, initialize the state
    // Note: props.open must be a getter to support two-way binding
    let isOpen = $state(true);
    setSidebar({
      open: () => isOpen,
      setOpen: (val) => { isOpen = val; }
    });
    
    // 2. In a child component, consume the state
    const sidebar = useSidebar();
    
    // Access properties directly (do not destructure)
    console.log(sidebar.open);
    console.log(sidebar.isMobile);
    
    // Use methods
    sidebar.toggle();