@adamscybot/react-leaflet-component-marker
Version:
A tiny wrapper for react-leaflet's <Marker /> component that allows you to use a React component as a marker, with working state, handlers, and access to parent contexts.
116 lines (115 loc) • 6.65 kB
TypeScript
import React, { type ReactElement, type ComponentType } from 'react';
import { type MarkerProps as ReactLeafletMarkerProps } from 'react-leaflet';
import { type DivIconOptions, type Marker as LeafletMarker } from 'leaflet';
import { type SetRequired } from 'type-fest';
/**
* The possible options for the {@link ComponentMarkerOpts.layoutMode | `componentIconOpts.layoutMode`} option.
**/
export type ComponentMarkerLayout = 'fit-content' | 'fit-parent';
export type ComponentMarkerRootDivIconOpts = Omit<DivIconOptions, 'html' | 'bgPos' | 'shadowUrl' | 'shadowSize' | 'shadowAnchor' | 'shadowRetinaUrl' | 'iconUrl' | 'iconRetinaUrl'>;
type RootDivOpt<RequiredKeys extends keyof ComponentMarkerRootDivIconOpts = never> = {
/**
* The {@link DivIconOptions} (except for the `html` property and other properties that are not relevant in the context of a React driven marker) that are to be supplied to the `div` wrapper for the leaflet-managed wrapper of the React icon component.
*
* When setting {@link ComponentMarkerOpts.layoutMode | `componentIconOpts.layoutMode`} as `'fit-parent'`, it is expected that {@link ComponentMarkerRootDivIconOpts.iconSize | `componentIconOpts.rootDivOpts.iconSize`} is set since this defines the dimensions of the component in this mode.
*
* @see {@link ComponentMarkerRootDivIconOpts}
**/
rootDivOpts: SetRequired<ComponentMarkerRootDivIconOpts, RequiredKeys>;
};
type CoreComponentMarkerOpts = {
/**
* `'fit-content'` disregards the `iconSize` passed to leaflet (defaults to `[0,0]`) and allows the React icon marker to be determined by the size of the provided component itself (which could be dynamic). Automatic alignment compensation is
* added to ensure the icon component stays centred on the X axis with the marker.
*
* `'fit-parent'` will set the container of the component to be the same size as the `iconSize`. Typically, this is used alongside a static icon size that is passed via {@link ComponentMarkerRootDivIconOpts.iconSize | `componentIconOpts.rootDivOpts.iconSize`}. This setup may allow for more granular control over positioning and anchor configuration. The user supplied Icon component itself should use a width and height of `100%` to fill the container.
*
* @defaultValue `"fit-content"`
* @see {@link ComponentMarkerLayout}
*/
layoutMode?: ComponentMarkerLayout;
/**
* If set to `true`, panning/scrolling the map will not be possible "through" the component marker.
*
* This applies to the entire component marker.
*
* @defaultValue `false`
*/
disableScrollPropagation?: boolean;
/**
* If set to `true`, clicking on the component marker will not be captured by the underlying map.
*
* This applies to the entire component marker. Note this will also disable the ability to activate native react-leaflet
* popups via clicking.
*
* @defaultValue `false`
*/
disableClickPropagation?: boolean;
/**
* Enable or disable the console warning about the case where the {@link BaseMarkerProps.componentIconOpts | `componentIconOpts`} prop was set but the {@link BaseMarkerProps.icon | `icon`} prop
* is not a component. This would mean those options are unused.
*
* @defaultValue `true`
* @see {@link BaseMarkerProps.componentIconOpts}
*/
unusedOptsWarning?: boolean;
/**
* Enable or disable the console warning about the case where {@link ComponentMarkerOpts.layoutMode | `componentIconOpts.layoutMode`} was set to `fit-parent` but the {@link ComponentMarkerRootDivIconOpts.iconSize | `componentIconOpts.rootDivOpts.iconSize`}
* has not been set. This would mean the size of the React component icon would not be visible.
*
* @defaultValue `true`
* @see {@link ComponentMarkerOpts.rootDivOpts}
* @see {@link ComponentMarkerRootDivIconOpts.iconSize}
*/
rootSizeWarning?: boolean;
};
type UnionKeys<T> = T extends T ? keyof T : never;
type StrictUnionHelper<T, TAll> = T extends any ? T & Partial<Record<Exclude<UnionKeys<TAll>, keyof T>, undefined>> : never;
type StrictUnion<T> = StrictUnionHelper<T, T>;
export type ComponentMarkerOpts = StrictUnion<(CoreComponentMarkerOpts & {
layoutMode?: 'fit-content';
} & Partial<RootDivOpt>) | (CoreComponentMarkerOpts & {
layoutMode: 'fit-parent';
} & RootDivOpt<'iconSize'>)>;
type BaseMarkerProps<AdditionalIconTypes = never> = Omit<ReactLeafletMarkerProps, 'icon'> & {
/** A {@link ReactElement} representing the Markers icon, or any type from [react-leaflet Marker](https://react-leaflet.js.org/docs/api-components/#marker) component. */
icon: ReactElement | AdditionalIconTypes;
/**
* The {@link ComponentMarkerOpts}. These will not be effective if {@link BaseMarkerProps.icon | `icon`} is not set to a React Element or Component, and a warning will be given in the console.
*
* @see {@link ComponentMarkerOpts.unusedOptsWarning}
*/
componentIconOpts?: ComponentMarkerOpts;
};
export type MarkerProps = BaseMarkerProps<ReactLeafletMarkerProps['icon'] | ComponentType>;
/**
* A modified version of the [react-leaflet Marker](https://react-leaflet.js.org/docs/api-components/#marker) component that is extended such that it allows a {@link ReactElement} to be used as the icon.
*
* @example
* Basic usage:
* ```
* // Define marker component
* const MarkerIconExample = () => {
* return (
* <>
* <button onClick={() => console.log("button 1 clicked")}>Button 1</button>
* <button onClick={() => console.log("button 2 clicked")}>Button 2</button>
* </>
* )
* }
*
* // Use marker component
* <Marker position={[51.505, -0.091]} icon={<MarkerIconExample />} />
* ```
**/
export declare const Marker: React.ForwardRefExoticComponent<Omit<ReactLeafletMarkerProps, "icon"> & {
/** A {@link ReactElement} representing the Markers icon, or any type from [react-leaflet Marker](https://react-leaflet.js.org/docs/api-components/#marker) component. */
icon: React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | import("leaflet").Icon<import("leaflet").IconOptions> | import("leaflet").DivIcon | React.ComponentType | undefined;
/**
* The {@link ComponentMarkerOpts}. These will not be effective if {@link BaseMarkerProps.icon | `icon`} is not set to a React Element or Component, and a warning will be given in the console.
*
* @see {@link ComponentMarkerOpts.unusedOptsWarning}
*/
componentIconOpts?: ComponentMarkerOpts;
} & React.RefAttributes<LeafletMarker<any>>>;
export {};