Iconpicker and Bugfixes

This commit is contained in:
Sergej Schwabauer 2022-03-04 11:50:41 +01:00
parent f6ec1e1639
commit b62e1a281e
13 changed files with 261 additions and 69 deletions

View File

@ -21,3 +21,5 @@ npm install @pnp/spfx-property-controls --save --save-exact
npm run serve npm run serve
gulp clean; gulp build; gulp bundle --ship; gulp package-solution --ship; gulp clean; gulp build; gulp bundle --ship; gulp package-solution --ship;
npm run serve npm run serve
exit
npm run serve

View File

@ -1,6 +1,6 @@
import * as React from 'react'; import * as React from 'react';
// import styles from './Autocomplete.module.scss'; import styles from './Autocomplete.module.scss';
import { TextField, ITextFieldProps, Callout, DirectionalHint, ITextField } from 'office-ui-fabric-react'; import { TextField, ITextFieldProps, Callout, ICalloutProps, DirectionalHint, ITextField, TextFieldBase } from 'office-ui-fabric-react';
import { isNullOrEmpty, cssClasses, getDeepOrDefault, isFunction } from '@spfxappdev/utility'; import { isNullOrEmpty, cssClasses, getDeepOrDefault, isFunction } from '@spfxappdev/utility';
@ -9,6 +9,9 @@ export interface IAutocompleteProps extends Omit<ITextFieldProps, "componentRef"
minValueLength?: number; minValueLength?: number;
onLoadSuggestions?(newValue: string): void; onLoadSuggestions?(newValue: string): void;
onRenderSuggestions?(): JSX.Element; onRenderSuggestions?(): JSX.Element;
textFieldRef?(fluentUITextField: ITextField, autocompleteComponent: Autocomplete, htmlInput?: HTMLInputElement);
onUpdated?(newValue: string);
calloutProps?: Omit<ICalloutProps, "hidden" | "target" | "preventDismissOnScroll" | "directionalHint" | "directionalHintFixed" | "isBeakVisible">;
} }
interface IAutocompleteState { interface IAutocompleteState {
@ -25,7 +28,10 @@ export class Autocomplete extends React.Component<IAutocompleteProps, IAutocompl
public static defaultProps: IAutocompleteProps = { public static defaultProps: IAutocompleteProps = {
showSuggestionsOnFocus: false, showSuggestionsOnFocus: false,
minValueLength: 3 minValueLength: 3,
calloutProps: {
gapSpace: 0
}
}; };
private textFieldReference: ITextField = null; private textFieldReference: ITextField = null;
@ -35,15 +41,22 @@ export class Autocomplete extends React.Component<IAutocompleteProps, IAutocompl
private userIsTyping: boolean = false; private userIsTyping: boolean = false;
private lastValue: string = ""; private lastValue: string = "";
private onUpdateValueText: string = "";
public render(): React.ReactElement<IAutocompleteProps> { public render(): React.ReactElement<IAutocompleteProps> {
return (<> return (<>
<TextField {...this.props} <TextField {...this.props}
className={cssClasses("styles.autocomplete", this.props.className)} autoComplete={"off"}
className={cssClasses(styles.autocomplete, this.props.className)}
componentRef={(input: ITextField) => { componentRef={(input: ITextField) => {
this.textFieldReference = input; this.textFieldReference = input;
this.textFieldDomElement = getDeepOrDefault<HTMLInputElement>(input, "_textElement.current", null); this.textFieldDomElement = getDeepOrDefault<HTMLInputElement>(input, "_textElement.current", null);
if(isFunction(this.props.textFieldRef)) {
this.props.textFieldRef(input, this, this.textFieldDomElement);
}
}} }}
onFocus={(ev: any) => { onFocus={(ev: any) => {
@ -64,12 +77,7 @@ export class Autocomplete extends React.Component<IAutocompleteProps, IAutocompl
} }
}} }}
onChange={(ev: any, newValue: string) => { onChange={(ev: any, newValue: string) => {
this.onValueChanged(ev, newValue);
this.onValueChanged(newValue);
if(isFunction(this.props.onChange)) {
this.props.onChange(ev, newValue);
}
}} }}
defaultValue={this.state.currentValue} defaultValue={this.state.currentValue}
/> />
@ -78,17 +86,45 @@ export class Autocomplete extends React.Component<IAutocompleteProps, IAutocompl
</>); </>);
} }
public updateValue(newValue: string): void {
this.onUpdateValueText = newValue;
this.setState({
currentValue: newValue
}, () => {
(this.textFieldReference as TextFieldBase).setState({
uncontrolledValue: this.onUpdateValueText
});
if(isFunction(this.props.onUpdated)) {
this.props.onUpdated(newValue);
}
});
}
private renderSuggesstionsFlyout(): JSX.Element { private renderSuggesstionsFlyout(): JSX.Element {
let minWidth: number = getDeepOrDefault<number>(this.props, "calloutProps.calloutMinWidth", -1);
if(minWidth <= 0) {
minWidth = getDeepOrDefault<number>(this, "textFieldDomElement.clientWidth", -1);
}
if(minWidth > 0) {
this.props.calloutProps.calloutMinWidth = minWidth;
}
return (<Callout return (<Callout
{...this.props.calloutProps}
hidden={!this.state.isFlyoutVisible} hidden={!this.state.isFlyoutVisible}
className={"styles.bagHeaderMegamenu"}
directionalHintFixed={true} directionalHintFixed={true}
gapSpace={0}
// calloutWidth={window.outerWidth}
isBeakVisible={false} isBeakVisible={false}
target={this.textFieldDomElement} target={this.textFieldDomElement}
onDismiss={() => { onDismiss={(ev?: any) => {
this.hideSuggesstionsFlyout(); this.hideSuggesstionsFlyout();
if(isFunction(this.props.calloutProps.onDismiss)) {
this.props.calloutProps.onDismiss(ev);
}
}} }}
preventDismissOnScroll={true} preventDismissOnScroll={true}
directionalHint={DirectionalHint.bottomCenter}> directionalHint={DirectionalHint.bottomCenter}>
@ -97,7 +133,7 @@ export class Autocomplete extends React.Component<IAutocompleteProps, IAutocompl
); );
} }
private onValueChanged(newValue: string): void { private onValueChanged(ev: any, newValue: string): void {
this.userIsTyping = true; this.userIsTyping = true;
this.state.currentValue = newValue; this.state.currentValue = newValue;
@ -106,11 +142,17 @@ export class Autocomplete extends React.Component<IAutocompleteProps, IAutocompl
}); });
this.handleSuggestionListVisibility(); this.handleSuggestionListVisibility();
if(isFunction(this.props.onChange)) {
this.props.onChange(ev, newValue);
}
} }
private onTextFieldBlur(): void { private onTextFieldBlur(): void {
this.userIsTyping = false; this.userIsTyping = false;
window.setTimeout(() => {
this.hideSuggesstionsFlyout();
}, 150);
} }
private handleSuggestionListVisibility(): void { private handleSuggestionListVisibility(): void {

View File

@ -1,4 +1,36 @@
@import '~@microsoft/sp-office-ui-fabric-core/dist/sass/SPFabricCore.scss';
.iconpicker { .iconpicker {
display: block; display: block;
}
.suggesstion {
display: block;
&-item {
display: flex;
padding: 5px 20px;
border-bottom: solid 1px $ms-color-themeLighter;
border-top: solid 1px $ms-color-themeLighter;
align-items: center;
cursor: pointer;
&:first-child {
border-top-width: 0;
}
&:last-child {
border-bottom-width: 0;
}
i {
padding-right: 20px;
font-size: 2em;
}
span {
font-size: 1em;
}
}
} }

View File

@ -1,26 +1,25 @@
import * as React from 'react'; import * as React from 'react';
import styles from './IconPicker.module.scss'; import styles from './IconPicker.module.scss';
import { ITextFieldProps, Icon } from 'office-ui-fabric-react'; import { Icon, ITextField } from 'office-ui-fabric-react';
import { allIcons } from './availableIcons'; import { allIcons } from './availableIcons';
import { isNullOrEmpty, cssClasses } from '@spfxappdev/utility'; import { isNullOrEmpty, cssClasses, isFunction } from '@spfxappdev/utility';
import { Autocomplete, IAutocompleteProps } from '@src/components/autocomplete/Autocomplete'; import { Autocomplete, IAutocompleteProps } from '@src/components/autocomplete/Autocomplete';
export interface IIconPickerProps extends Omit<ITextFieldProps, "componentRef">, IAutocompleteProps { export interface IIconPickerProps extends Omit<IAutocompleteProps, "onUpdated" | "onChange"> {
enableDialogPicker?: boolean; enableDialogPicker?: boolean;
dialogPickerIconName?: string; dialogPickerIconName?: string;
onIconChanged?(iconName: string): void;
} }
interface IIconPickerState { interface IIconPickerState {
currentValue: string; currentValue: string;
isFlyoutVisible: boolean;
} }
export class IconPicker extends React.Component<IIconPickerProps, IIconPickerState> { export class IconPicker extends React.Component<IIconPickerProps, IIconPickerState> {
public state: IIconPickerState = { public state: IIconPickerState = {
currentValue: isNullOrEmpty(this.props.defaultValue) ? "" : this.props.defaultValue, currentValue: isNullOrEmpty(this.props.defaultValue) ? "" : this.props.defaultValue
isFlyoutVisible: false,
}; };
public static defaultProps: IIconPickerProps = { public static defaultProps: IIconPickerProps = {
@ -29,18 +28,42 @@ export class IconPicker extends React.Component<IIconPickerProps, IIconPickerSta
showSuggestionsOnFocus: false, showSuggestionsOnFocus: false,
minValueLength: 0 minValueLength: 0
}; };
private inputValueOnClick: string = "";
private textFieldReference: ITextField = null;
private textFieldDomElement: HTMLInputElement = null;
private autocompleteRef: Autocomplete = null;
public render(): React.ReactElement<IIconPickerProps> { public render(): React.ReactElement<IIconPickerProps> {
return (<> return (<>
<Autocomplete {...this.props} <Autocomplete {...this.props}
textFieldRef={(fluentUITextField: ITextField, autocompleteComponent: Autocomplete, htmlInput: HTMLInputElement) => {
this.textFieldReference = fluentUITextField;
this.textFieldDomElement = htmlInput;
this.autocompleteRef = autocompleteComponent;
if(isFunction(this.props.textFieldRef)) {
this.props.textFieldRef(fluentUITextField, autocompleteComponent, this.textFieldDomElement);
}
}}
onChange={(ev: any, name: string) => {
if(isFunction(this.props.onIconChanged)) {
this.props.onIconChanged(name);
}
}}
onUpdated={(name: string) => {
if(isFunction(this.props.onIconChanged)) {
this.props.onIconChanged(name);
}
}}
className={cssClasses(styles.iconpicker)} className={cssClasses(styles.iconpicker)}
defaultValue={this.state.currentValue} defaultValue={this.state.currentValue}
onLoadSuggestions={(newValue: string) => { onLoadSuggestions={(newValue: string) => {
this.state.currentValue = newValue;
this.setState({ this.setState({
currentValue: newValue, currentValue: newValue
isFlyoutVisible: true
}); });
}} }}
onRenderSuggestions={() => { onRenderSuggestions={() => {
@ -55,14 +78,27 @@ export class IconPicker extends React.Component<IIconPickerProps, IIconPickerSta
} }
private renderSuggesstionsFlyout(): JSX.Element { private renderSuggesstionsFlyout(): JSX.Element {
return (<> return (
<ul> <div className={styles["suggesstion"]}>
{allIcons.Where(icon => icon.StartsWith(this.state.currentValue)).map((iconName: string): JSX.Element => { {allIcons.Where(icon => icon.StartsWith(this.state.currentValue)).map((iconName: string): JSX.Element => {
return (<li key={`Icon_${iconName}`}><Icon iconName={iconName} /></li>); return (<div
key={`Icon_${iconName}`}
onClick={() => {
this.inputValueOnClick = iconName;
this.setState({
currentValue: iconName
});
this.autocompleteRef.updateValue(iconName);
}}
className={styles["suggesstion-item"]}>
<Icon iconName={iconName} />
<span>{iconName}</span>
</div>);
})} })}
</ul> </div>
</>
); );
} }
} }

View File

@ -34,7 +34,8 @@
"markerCategories": [], "markerCategories": [],
"center": [51.505, -0.09], "center": [51.505, -0.09],
"startZoom": 13, "startZoom": 13,
"maxZoom": 50, "maxZoom": 30,
"minZoom": 1,
"dragging": true, "dragging": true,
"height": 400, "height": 400,
"scrollWheelZoom": true, "scrollWheelZoom": true,

View File

@ -6,9 +6,7 @@ import {
PropertyPaneTextField, PropertyPaneTextField,
PropertyPaneToggle, PropertyPaneToggle,
PropertyPaneSlider, PropertyPaneSlider,
PropertyPaneButton, PropertyPaneButton
PropertyPaneLabel
} from '@microsoft/sp-property-pane'; } from '@microsoft/sp-property-pane';
import { PropertyPaneWebPartInformation } from '@pnp/spfx-property-controls/lib/PropertyPaneWebPartInformation'; import { PropertyPaneWebPartInformation } from '@pnp/spfx-property-controls/lib/PropertyPaneWebPartInformation';
import { BaseClientSideWebPart } from '@microsoft/sp-webpart-base'; import { BaseClientSideWebPart } from '@microsoft/sp-webpart-base';
@ -19,6 +17,7 @@ import Map from './components/Map';
import { IMapProps, IMarker, IMarkerCategory } from './components/IMapProps'; import { IMapProps, IMarker, IMarkerCategory } from './components/IMapProps';
import ManageMarkerCategoriesDialog, { IManageMarkerCategoriesDialogProps } from './components/ManageMarkerCategoriesDialog'; import ManageMarkerCategoriesDialog, { IManageMarkerCategoriesDialogProps } from './components/ManageMarkerCategoriesDialog';
import { isNullOrEmpty } from '@spfxappdev/utility'; import { isNullOrEmpty } from '@spfxappdev/utility';
import { Spinner, ISpinnerProps } from '@microsoft/office-ui-fabric-react-bundle';
export interface IMapPlugins { export interface IMapPlugins {
searchBox: boolean; searchBox: boolean;
@ -34,6 +33,7 @@ export interface IMapWebPartProps {
center: [number, number]; center: [number, number];
startZoom: number; startZoom: number;
maxZoom: number; maxZoom: number;
minZoom: number;
height: number; height: number;
scrollWheelZoom: boolean; scrollWheelZoom: boolean;
dragging: boolean; dragging: boolean;
@ -49,11 +49,10 @@ export default class MapWebPart extends BaseClientSideWebPart<IMapWebPartProps>
private _isDarkTheme: boolean = false; private _isDarkTheme: boolean = false;
protected onInit(): Promise<void> { protected onInit(): Promise<void> {
return super.onInit(); return super.onInit();
} }
public render(): void { public render(): void {
const element: React.ReactElement<IMapProps> = React.createElement( const element: React.ReactElement<IMapProps> = React.createElement(
Map, Map,
{ {
@ -61,6 +60,8 @@ export default class MapWebPart extends BaseClientSideWebPart<IMapWebPartProps>
markerCategories: this.properties.markerCategories||[], markerCategories: this.properties.markerCategories||[],
isEditMode: this.displayMode == DisplayMode.Edit, isEditMode: this.displayMode == DisplayMode.Edit,
zoom: this.properties.startZoom, zoom: this.properties.startZoom,
minZoom: this.properties.minZoom,
maxZoom: this.properties.maxZoom,
center: this.properties.center, center: this.properties.center,
title: this.properties.title, title: this.properties.title,
height: this.properties.height, height: this.properties.height,
@ -91,9 +92,37 @@ export default class MapWebPart extends BaseClientSideWebPart<IMapWebPartProps>
ReactDom.render(element, this.domElement); ReactDom.render(element, this.domElement);
} }
// protected onPropertyPaneFieldChanged(propertyPath: string, oldValue: any, newValue: any): void { protected onDisplayModeChanged(oldDisplayMode: DisplayMode): void {
// super.onPropertyPaneFieldChanged(propertyPath, oldValue, newValue) this.reload();
// console.log() }
protected onPropertyPaneFieldChanged(propertyPath: string, oldValue: any, newValue: any): void {
super.onPropertyPaneFieldChanged(propertyPath, oldValue, newValue);
const reloadIfOneOfProps = ["height", "tileLayerUrl", "minZoom", "maxZoom", "tileLayerAttribution", "plugins.zoomControl"]
if(reloadIfOneOfProps.Contains(p => p.Equals(propertyPath))) {
this.reload();
}
}
private reload(): void {
setTimeout(() => {
const spinner: React.ReactElement<ISpinnerProps> = React.createElement(Spinner, {
});
ReactDom.render(spinner, this.domElement);
setTimeout(() => { this.render(); }, 300);
}, 500);
}
// protected get disableReactivePropertyChanges(): boolean {
// return true;
// } // }
private onMarkerCategoriesChanged(markerCategories: IMarkerCategory[]): void { private onMarkerCategoriesChanged(markerCategories: IMarkerCategory[]): void {
@ -131,12 +160,15 @@ export default class MapWebPart extends BaseClientSideWebPart<IMapWebPartProps>
{ {
groupName: strings.WebPartPropertyGroupMapSettings, groupName: strings.WebPartPropertyGroupMapSettings,
groupFields: [ groupFields: [
// PropertyPaneToggle('plugins.searchBox', { // PropertyPaneWebPartInformation({
// label: "searchBox" // description: `<div class='wp-settings-info'>${strings.WebPartPropertySettingsInfoLabel}</div>`,
// key: 'Info_For_3f860b48-1dc3-496d-bd28-b145672289cc'
// }), // }),
PropertyPaneWebPartInformation({ PropertyPaneSlider('minZoom', {
description: `<div class='wp-settings-info'>${strings.WebPartPropertySettingsInfoLabel}</div>`, label: strings.WebPartPropertyMinZoomLabel,
key: 'Info_For_3f860b48-1dc3-496d-bd28-b145672289cc' max: 30,
min: 0,
step: 1
}), }),
PropertyPaneSlider('maxZoom', { PropertyPaneSlider('maxZoom', {
label: strings.WebPartPropertyMaxZoomLabel, label: strings.WebPartPropertyMaxZoomLabel,
@ -150,12 +182,6 @@ export default class MapWebPart extends BaseClientSideWebPart<IMapWebPartProps>
max: 1200, max: 1200,
step: 50 step: 50
}), }),
PropertyPaneTextField('tileLayerUrl', {
label: strings.WebPartPropertyTileLayerUrlLabel
}),
PropertyPaneTextField('tileLayerAttribution', {
label: strings.WebPartPropertyTileLayerAttributionLabel
}),
PropertyPaneToggle('scrollWheelZoom', { PropertyPaneToggle('scrollWheelZoom', {
label: strings.WebPartPropertyScrollWheelZoomLabel, label: strings.WebPartPropertyScrollWheelZoomLabel,
}), }),
@ -168,10 +194,29 @@ export default class MapWebPart extends BaseClientSideWebPart<IMapWebPartProps>
] ]
}, },
{
isCollapsed: true,
groupName: strings.WebPartPropertyGroupTileLayerSettings,
groupFields: [
PropertyPaneWebPartInformation({
description: `<div class='wp-settings-info'>${strings.WebPartPropertyTileLayerUrlInformationLabel}</div>`,
key: 'Tile_For_3f860b48-1dc3-496d-bd28-b145672289cc'
}),
PropertyPaneTextField('tileLayerUrl', {
label: strings.WebPartPropertyTileLayerUrlLabel
}),
PropertyPaneTextField('tileLayerAttribution', {
label: strings.WebPartPropertyTileLayerAttributionLabel
}),
]
},
{ {
isCollapsed: true, isCollapsed: true,
groupName: strings.WebPartPropertyGroupPlugins, groupName: strings.WebPartPropertyGroupPlugins,
groupFields: [ groupFields: [
// PropertyPaneToggle('plugins.searchBox', {
// label: "searchBox"
// }),
PropertyPaneToggle('plugins.markercluster', { PropertyPaneToggle('plugins.markercluster', {
label: strings.WebPartPropertyPluginMarkerClusterLabel, label: strings.WebPartPropertyPluginMarkerClusterLabel,
}), }),
@ -216,7 +261,7 @@ export default class MapWebPart extends BaseClientSideWebPart<IMapWebPartProps>
groupFields: [ groupFields: [
PropertyPaneWebPartInformation({ PropertyPaneWebPartInformation({
description: `<h3>Author</h3> description: `<h3>Author</h3>
<a href='https://spfx-app.dev/'>SPFx-App.dev</a> <a href='https://spfx-app.dev/' data-interception="off" target='_blank'>SPFx-App.dev</a>
<h3>Version</h3> <h3>Version</h3>
${this.context.manifest.version} ${this.context.manifest.version}
<h3>Web Part Instance id</h3> <h3>Web Part Instance id</h3>

View File

@ -13,6 +13,7 @@ import '@spfxappdev/utility/lib/extensions/ArrayExtensions';
import ManageMarkerCategoriesDialog from './ManageMarkerCategoriesDialog'; import ManageMarkerCategoriesDialog from './ManageMarkerCategoriesDialog';
import { MarkerIcon } from './MarkerIcon'; import { MarkerIcon } from './MarkerIcon';
import * as strings from 'MapWebPartStrings'; import * as strings from 'MapWebPartStrings';
import { IconPicker } from '@src/components/iconPicker/IconPicker';
export interface IAddOrEditPanelProps { export interface IAddOrEditPanelProps {
markerItem: IMarker; markerItem: IMarker;
@ -192,13 +193,26 @@ export default class AddOrEditPanel extends React.Component<IAddOrEditPanelProps
}} }}
/> />
<TextField label={strings.LabelIcon} description={strings.LabelLeaveEmpty} defaultValue={this.state.markerItem.iconProperties.iconName} onChange={(ev: any, iconName: string) => { {/* <TextField label={strings.LabelIcon} description={strings.LabelLeaveEmpty} defaultValue={this.state.markerItem.iconProperties.iconName} onChange={(ev: any, iconName: string) => {
this.state.markerItem.iconProperties.iconName = iconName; this.state.markerItem.iconProperties.iconName = iconName;
this.setState({ this.setState({
markerItem: this.state.markerItem, markerItem: this.state.markerItem,
isSaveButtonDisabled: false isSaveButtonDisabled: false
}); });
}} /> }} /> */}
<IconPicker
label={strings.LabelIcon}
description={strings.LabelLeaveEmpty}
defaultValue={this.state.markerItem.iconProperties.iconName}
onIconChanged={(iconName: string) => {
this.state.markerItem.iconProperties.iconName = iconName;
this.setState({
markerItem: this.state.markerItem,
isSaveButtonDisabled: false
});
}}
/>
<InlineColorPicker <InlineColorPicker
label={strings.LabelIconColor} label={strings.LabelIconColor}

View File

@ -50,6 +50,7 @@ export interface IMapProps {
zoom?: number; zoom?: number;
center?: [number, number]; center?: [number, number];
maxZoom?: number; maxZoom?: number;
minZoom?: number;
title?: string; title?: string;
height: number; height: number;
dragging: boolean; dragging: boolean;

View File

@ -165,11 +165,11 @@ export default class ManageMarkerCategoriesDialog extends React.Component<IManag
</div> </div>
<div className='spfxappdev-grid-col spfxappdev-sm3'> <div className='spfxappdev-grid-col spfxappdev-sm3'>
<IconPicker <IconPicker
defaultValue={categoryItem.iconProperties.iconName} defaultValue={categoryItem.iconProperties.iconName}
onChange={(ev: any, name: string) => { onIconChanged={(name: string) => {
this.state.markerCategories[index].iconProperties.iconName = name; this.state.markerCategories[index].iconProperties.iconName = name;
this.validateForm(); this.validateForm();
}} }}
/> />
</div> </div>
<div className='spfxappdev-grid-col spfxappdev-sm1'> <div className='spfxappdev-grid-col spfxappdev-sm1'>

View File

@ -20,6 +20,11 @@
} }
} }
.display-mode .marker-type-none {
cursor: default !important;
}
.manage-categories-label { .manage-categories-label {
font-size: 10px; font-size: 10px;
color: #1190F4; color: #1190F4;
@ -143,7 +148,7 @@
border-bottom: solid 1px #aeaeae; border-bottom: solid 1px #aeaeae;
&:nth-child(odd) { &:nth-child(odd) {
background: $ms-color-themeLight; background: $ms-color-themeLighter;
} }
} }
} }

View File

@ -8,7 +8,7 @@ import "leaflet/dist/leaflet.css";
import "react-leaflet-markercluster/dist/styles.min.css"; import "react-leaflet-markercluster/dist/styles.min.css";
import * as L from 'leaflet'; import * as L from 'leaflet';
import { ContextualMenu, IContextualMenuItem, Panel, Dialog, IPanelProps, DefaultButton, PanelType, DialogType, DialogContent, Label, Separator, PrimaryButton } from 'office-ui-fabric-react'; import { ContextualMenu, IContextualMenuItem, Panel, Dialog, IPanelProps, DefaultButton, PanelType, DialogType, DialogContent, Label, Separator, PrimaryButton } from 'office-ui-fabric-react';
import { isset, isNullOrEmpty, getDeepOrDefault } from '@spfxappdev/utility'; import { isset, isNullOrEmpty, getDeepOrDefault, cssClasses } from '@spfxappdev/utility';
import '@spfxappdev/utility/lib/extensions/StringExtensions'; import '@spfxappdev/utility/lib/extensions/StringExtensions';
import '@spfxappdev/utility/lib/extensions/ArrayExtensions'; import '@spfxappdev/utility/lib/extensions/ArrayExtensions';
import { DisplayMode } from '@microsoft/sp-core-library'; import { DisplayMode } from '@microsoft/sp-core-library';
@ -86,7 +86,8 @@ export default class Map extends React.Component<IMapProps, IMapState> {
public render(): React.ReactElement<IMapProps> { public render(): React.ReactElement<IMapProps> {
this.allLeafletMarker = {}; this.allLeafletMarker = {};
const isZoomControlEnabled: boolean = this.props.isEditMode ? true : getDeepOrDefault<boolean>(this.props, "plugins.zoomControl", true); // const isZoomControlEnabled: boolean = this.props.isEditMode ? true : getDeepOrDefault<boolean>(this.props, "plugins.zoomControl", true);
const isZoomControlEnabled: boolean = getDeepOrDefault<boolean>(this.props, "plugins.zoomControl", true);
const isScrollWheelZoomEnabled: boolean = this.props.isEditMode ? true : getDeepOrDefault<boolean>(this.props, "scrollWheelZoom", true); const isScrollWheelZoomEnabled: boolean = this.props.isEditMode ? true : getDeepOrDefault<boolean>(this.props, "scrollWheelZoom", true);
const isDraggingEnabled: boolean = this.props.isEditMode ? true : getDeepOrDefault<boolean>(this.props, "dragging", true); const isDraggingEnabled: boolean = this.props.isEditMode ? true : getDeepOrDefault<boolean>(this.props, "dragging", true);
// //
@ -99,15 +100,16 @@ export default class Map extends React.Component<IMapProps, IMapState> {
} }
<MapContainer <MapContainer
className={this.props.isEditMode ? "edit-mode" : "display-mode"}
zoomControl={isZoomControlEnabled} zoomControl={isZoomControlEnabled}
center={this.props.center} center={this.props.center}
zoom={this.props.zoom} zoom={this.props.zoom}
maxZoom={this.props.maxZoom} maxZoom={this.props.maxZoom}
minZoom={this.props.minZoom}
scrollWheelZoom={isScrollWheelZoomEnabled} scrollWheelZoom={isScrollWheelZoomEnabled}
touchZoom={isScrollWheelZoomEnabled} touchZoom={isScrollWheelZoomEnabled}
doubleClickZoom={isScrollWheelZoomEnabled} doubleClickZoom={isScrollWheelZoomEnabled}
dragging={isDraggingEnabled} dragging={isDraggingEnabled}
whenCreated={(map: L.Map) => { whenCreated={(map: L.Map) => {
map.on("contextmenu", (ev: L.LeafletEvent) => { map.on("contextmenu", (ev: L.LeafletEvent) => {
@ -125,6 +127,10 @@ export default class Map extends React.Component<IMapProps, IMapState> {
}); });
}); });
map.on("zoom", (ev: any) => {
console.log("SSC", this.props.maxZoom, ev);
})
this.map = map; this.map = map;
} }
} }
@ -502,12 +508,14 @@ export default class Map extends React.Component<IMapProps, IMapState> {
shadowSize: null, shadowSize: null,
shadowAnchor: null, shadowAnchor: null,
iconSize: new L.Point(27, 36), iconSize: new L.Point(27, 36),
className: 'leaflet-div-icon' className: cssClasses('leaflet-div-icon', `marker-type-${marker.type.toLowerCase()}`)
}); });
markerIcon.createIcon = (oldIcon: HTMLElement) => { markerIcon.createIcon = (oldIcon: HTMLElement) => {
const wrapper = document.createElement("div"); const wrapper = document.createElement("div");
wrapper.classList.add("leaflet-marker-icon"); wrapper.classList.add("leaflet-marker-icon");
wrapper.classList.add(`marker-type-${marker.type.toLowerCase()}`);
wrapper.dataset.markerid = marker.id; wrapper.dataset.markerid = marker.id;
wrapper.style.marginLeft = (markerIcon.options.iconAnchor[0] * -1) + "px"; wrapper.style.marginLeft = (markerIcon.options.iconAnchor[0] * -1) + "px";

View File

@ -1,6 +1,7 @@
define([], function() { define([], function() {
return { return {
"WebPartPropertyGroupMapSettings": "General settings", "WebPartPropertyGroupMapSettings": "General settings",
"WebPartPropertyGroupTileLayerSettings": "Tile Layer",
"WebPartPropertyGroupPlugins": "Plugins/Controls", "WebPartPropertyGroupPlugins": "Plugins/Controls",
"WebPartPropertyGroupCategories": "Categories", "WebPartPropertyGroupCategories": "Categories",
"WebPartPropertyGroupAbout": "About", "WebPartPropertyGroupAbout": "About",
@ -9,13 +10,15 @@ define([], function() {
"WebPartPropertyPluginLegendLabel": "Show legend", "WebPartPropertyPluginLegendLabel": "Show legend",
"WebPartPropertyButtonManageCategories": "Manage categories", "WebPartPropertyButtonManageCategories": "Manage categories",
"WebPartPropertyPluginZoomControlLabel": "Show zoom control", "WebPartPropertyPluginZoomControlLabel": "Show zoom control",
"WebPartPropertyScrollWheelZoomLabel": "Enable zoom on mouse wheel/touch", "WebPartPropertyScrollWheelZoomLabel": "Enable zoom on mouse wheel/touch (only in display mode)",
"WebPartPropertyMapDraggingLabel": "Enable dragging in map", "WebPartPropertyMapDraggingLabel": "Enable dragging in map (only in display mode)",
"WebPartPropertyShowPopUpLabel": "Show tooltip when hovering the marker", "WebPartPropertyShowPopUpLabel": "Show tooltip when hovering the marker",
"WebPartPropertySettingsInfoLabel": "Most of these settings take effect only after a page refresh and only in display mode", "WebPartPropertySettingsInfoLabel": "Most of these settings take effect only after a page refresh and only in display mode",
"WebPartPropertyMaxZoomLabel": "Maximum zoom level (depends on Tile layer)", "WebPartPropertyMinZoomLabel": "Minimum zoom level (zoom out)",
"WebPartPropertyHeightLabel": "Map height", "WebPartPropertyMaxZoomLabel": "Maximum zoom level (zoom in, depends on Tile layer)",
"WebPartPropertyTileLayerUrlLabel": "Tile layer URL (Examples can be found here: https://leaflet-extras.github.io/leaflet-providers/preview/)", "WebPartPropertyHeightLabel": "Map height (in px)",
"WebPartPropertyTileLayerUrlInformationLabel": "In this section you can change the tile layer and attribution. You can find more tile layers e.g. <a href='https://leaflet-extras.github.io/leaflet-providers/preview/' data-interception='off' target='_blank'>here</a>",
"WebPartPropertyTileLayerUrlLabel": "Tile layer URL",
"WebPartPropertyTileLayerAttributionLabel": "Tile layer attribution", "WebPartPropertyTileLayerAttributionLabel": "Tile layer attribution",
"ContextMenuAddNewMarkerLabel": "Add a new marker here", "ContextMenuAddNewMarkerLabel": "Add a new marker here",
"ContextMenuSetStartPositionLabel": "Make this view as start position", "ContextMenuSetStartPositionLabel": "Make this view as start position",

View File

@ -1,5 +1,6 @@
declare interface IMapWebPartStrings { declare interface IMapWebPartStrings {
WebPartPropertyGroupMapSettings: string; WebPartPropertyGroupMapSettings: string;
WebPartPropertyGroupTileLayerSettings: string;
WebPartPropertyGroupPlugins: string; WebPartPropertyGroupPlugins: string;
WebPartPropertyGroupCategories: string; WebPartPropertyGroupCategories: string;
WebPartPropertyGroupAbout: string; WebPartPropertyGroupAbout: string;
@ -12,8 +13,10 @@ declare interface IMapWebPartStrings {
WebPartPropertyMapDraggingLabel: string; WebPartPropertyMapDraggingLabel: string;
WebPartPropertyShowPopUpLabel: string; WebPartPropertyShowPopUpLabel: string;
WebPartPropertySettingsInfoLabel: string; WebPartPropertySettingsInfoLabel: string;
WebPartPropertyMinZoomLabel: string;
WebPartPropertyMaxZoomLabel: string; WebPartPropertyMaxZoomLabel: string;
WebPartPropertyHeightLabel: string; WebPartPropertyHeightLabel: string;
WebPartPropertyTileLayerUrlInformationLabel: string;
WebPartPropertyTileLayerUrlLabel: string; WebPartPropertyTileLayerUrlLabel: string;
WebPartPropertyTileLayerAttributionLabel: string; WebPartPropertyTileLayerAttributionLabel: string;
ContextMenuAddNewMarkerLabel: string; ContextMenuAddNewMarkerLabel: string;