leaflet-anvil 0.2.2 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -94,6 +94,7 @@ Activation via `anvil.enable(AnvilMode.Name)` or through the UI toolbar:
94
94
  | `modeStyles` | `object` | `{}` | Per-mode style overrides for drawing, handles and active selection/highlight states. |
95
95
  | `controlPosition` | `string` | `'topleft'` | Determines the position of the toolbar on the map (e.g., `'topright'`). |
96
96
  | `modes` | `Array` | `All` | Defines which buttons appear in the toolbar. Supports nested arrays for button groups (blocks). |
97
+ | `modeTooltips` | `object \\| function` | `Defaults` | Overrides toolbar button tooltips. Missing values keep the built-in defaults. Use a function if translations can change after initialization. |
97
98
 
98
99
  ### Mode-Specific Styles
99
100
 
@@ -170,6 +171,30 @@ const anvil = new Anvil(map, {
170
171
  If you want to style the active highlight of interaction modes, use `selectionPathOptions`.
171
172
  That is what controls the temporary emphasis color when a geometry is selected in modes such as `Edit` or while actively transforming it in `Drag`, `Scale`, `Rotate`, `Union` or `Subtract`.
172
173
 
174
+ ### Custom Toolbar Tooltips
175
+
176
+ For simple overrides, pass a static map:
177
+
178
+ ```typescript
179
+ const anvil = new Anvil(map, {
180
+ modeTooltips: {
181
+ [AnvilMode.Edit]: 'Edit geometry',
182
+ [AnvilMode.Delete]: 'Remove geometry',
183
+ [AnvilMode.Off]: 'Disable tools',
184
+ },
185
+ });
186
+ ```
187
+
188
+ If your app language can change after `Anvil` has been created, pass a resolver instead.
189
+ The toolbar re-reads the tooltip when a button is focused or hovered, so the next interaction uses the current language without recreating the control.
190
+ The second argument is the built-in default tooltip, so you can preserve defaults without duplicating them.
191
+
192
+ ```typescript
193
+ const anvil = new Anvil(map, {
194
+ modeTooltips: (mode, fallback) => i18n.t(`mapTools.${mode}`, fallback),
195
+ });
196
+ ```
197
+
173
198
  ### Events (`ANVIL_EVENTS`)
174
199
 
175
200
  All events are fired on the Leaflet map:
package/dist/index.d.mts CHANGED
@@ -25,6 +25,8 @@ interface Mode {
25
25
  enable(): void;
26
26
  disable(): void;
27
27
  }
28
+ type AnvilModeTooltips = Partial<Record<AnvilMode, string>>;
29
+ type AnvilModeTooltipResolver = (mode: AnvilMode, defaultTooltip: string) => string | null | undefined;
28
30
  interface AnvilOptions {
29
31
  layerGroup?: L.FeatureGroup;
30
32
  snapping?: boolean;
@@ -38,6 +40,7 @@ interface AnvilOptions {
38
40
  modeStyles?: AnvilModeStyles;
39
41
  controlPosition?: L.ControlPosition;
40
42
  modes?: (AnvilMode | AnvilMode[])[];
43
+ modeTooltips?: AnvilModeTooltips | AnvilModeTooltipResolver;
41
44
  }
42
45
  interface AnvilModeStyleOptions {
43
46
  pathOptions?: L.PathOptions;
@@ -72,6 +75,7 @@ declare const ANVIL_EVENTS: {
72
75
  interface AnvilControlOptions extends L.ControlOptions {
73
76
  position?: L.ControlPosition;
74
77
  modes?: (AnvilMode | AnvilMode[])[];
78
+ modeTooltips?: AnvilModeTooltips | AnvilModeTooltipResolver;
75
79
  }
76
80
  declare class AnvilControl extends L.Control {
77
81
  private _btns;
@@ -82,4 +86,4 @@ declare class AnvilControl extends L.Control {
82
86
  }
83
87
  declare function anvilControl(anvil: Anvil, options?: AnvilControlOptions): AnvilControl;
84
88
 
85
- export { ANVIL_EVENTS, Anvil, AnvilControl, type AnvilControlOptions, AnvilMode, type AnvilModeStyleOptions, type AnvilModeStyles, type AnvilOptions, type Mode, anvilControl };
89
+ export { ANVIL_EVENTS, Anvil, AnvilControl, type AnvilControlOptions, AnvilMode, type AnvilModeStyleOptions, type AnvilModeStyles, type AnvilModeTooltipResolver, type AnvilModeTooltips, type AnvilOptions, type Mode, anvilControl };
package/dist/index.d.ts CHANGED
@@ -25,6 +25,8 @@ interface Mode {
25
25
  enable(): void;
26
26
  disable(): void;
27
27
  }
28
+ type AnvilModeTooltips = Partial<Record<AnvilMode, string>>;
29
+ type AnvilModeTooltipResolver = (mode: AnvilMode, defaultTooltip: string) => string | null | undefined;
28
30
  interface AnvilOptions {
29
31
  layerGroup?: L.FeatureGroup;
30
32
  snapping?: boolean;
@@ -38,6 +40,7 @@ interface AnvilOptions {
38
40
  modeStyles?: AnvilModeStyles;
39
41
  controlPosition?: L.ControlPosition;
40
42
  modes?: (AnvilMode | AnvilMode[])[];
43
+ modeTooltips?: AnvilModeTooltips | AnvilModeTooltipResolver;
41
44
  }
42
45
  interface AnvilModeStyleOptions {
43
46
  pathOptions?: L.PathOptions;
@@ -72,6 +75,7 @@ declare const ANVIL_EVENTS: {
72
75
  interface AnvilControlOptions extends L.ControlOptions {
73
76
  position?: L.ControlPosition;
74
77
  modes?: (AnvilMode | AnvilMode[])[];
78
+ modeTooltips?: AnvilModeTooltips | AnvilModeTooltipResolver;
75
79
  }
76
80
  declare class AnvilControl extends L.Control {
77
81
  private _btns;
@@ -82,4 +86,4 @@ declare class AnvilControl extends L.Control {
82
86
  }
83
87
  declare function anvilControl(anvil: Anvil, options?: AnvilControlOptions): AnvilControl;
84
88
 
85
- export { ANVIL_EVENTS, Anvil, AnvilControl, type AnvilControlOptions, AnvilMode, type AnvilModeStyleOptions, type AnvilModeStyles, type AnvilOptions, type Mode, anvilControl };
89
+ export { ANVIL_EVENTS, Anvil, AnvilControl, type AnvilControlOptions, AnvilMode, type AnvilModeStyleOptions, type AnvilModeStyles, type AnvilModeTooltipResolver, type AnvilModeTooltips, type AnvilOptions, type Mode, anvilControl };
package/dist/index.js CHANGED
@@ -2216,25 +2216,26 @@ var L21 = __toESM(require("leaflet"));
2216
2216
  var import_lucide = require("lucide");
2217
2217
  var ANVIL_TOOLBAR_STYLE_ID = "anvil-toolbar-styles";
2218
2218
  var MODE_CONFIGS = [
2219
- { id: "marker" /* Marker */, title: "Marker", icon: import_lucide.MapPin },
2220
- { id: "polyline" /* Polyline */, title: "Line", icon: import_lucide.Waypoints },
2221
- { id: "polygon" /* Polygon */, title: "Polygon", icon: import_lucide.Pentagon },
2222
- { id: "rectangle" /* Rectangle */, title: "Rectangle", icon: import_lucide.RectangleHorizontal },
2223
- { id: "square" /* Square */, title: "Square", icon: import_lucide.Square },
2224
- { id: "triangle" /* Triangle */, title: "Triangle", icon: import_lucide.Triangle },
2225
- { id: "circle" /* Circle */, title: "Circle", icon: import_lucide.Circle },
2226
- { id: "freehand" /* Freehand */, title: "Freehand", icon: import_lucide.Hand },
2227
- { id: "cut" /* Cut */, title: "Cut", icon: import_lucide.Scissors },
2228
- { id: "split" /* Split */, title: "Split", icon: import_lucide.Split },
2229
- { id: "union" /* Union */, title: "Union", icon: import_lucide.SquaresUnite },
2230
- { id: "subtract" /* Subtract */, title: "Subtract", icon: import_lucide.SquaresSubtract },
2231
- { id: "drag" /* Drag */, title: "Drag", icon: import_lucide.Move },
2232
- { id: "scale" /* Scale */, title: "Scale", icon: import_lucide.Scaling },
2233
- { id: "rotate" /* Rotate */, title: "Rotate", icon: import_lucide.RotateCw },
2234
- { id: "edit" /* Edit */, title: "Edit", icon: import_lucide.SquarePen },
2235
- { id: "delete" /* Delete */, title: "Delete", icon: import_lucide.Trash2 },
2236
- { id: "off" /* Off */, title: "Turn Off", icon: import_lucide.Power }
2219
+ { id: "marker" /* Marker */, defaultTooltip: "Marker", icon: import_lucide.MapPin },
2220
+ { id: "polyline" /* Polyline */, defaultTooltip: "Line", icon: import_lucide.Waypoints },
2221
+ { id: "polygon" /* Polygon */, defaultTooltip: "Polygon", icon: import_lucide.Pentagon },
2222
+ { id: "rectangle" /* Rectangle */, defaultTooltip: "Rectangle", icon: import_lucide.RectangleHorizontal },
2223
+ { id: "square" /* Square */, defaultTooltip: "Square", icon: import_lucide.Square },
2224
+ { id: "triangle" /* Triangle */, defaultTooltip: "Triangle", icon: import_lucide.Triangle },
2225
+ { id: "circle" /* Circle */, defaultTooltip: "Circle", icon: import_lucide.Circle },
2226
+ { id: "freehand" /* Freehand */, defaultTooltip: "Freehand", icon: import_lucide.Hand },
2227
+ { id: "cut" /* Cut */, defaultTooltip: "Cut", icon: import_lucide.Scissors },
2228
+ { id: "split" /* Split */, defaultTooltip: "Split", icon: import_lucide.Split },
2229
+ { id: "union" /* Union */, defaultTooltip: "Union", icon: import_lucide.SquaresUnite },
2230
+ { id: "subtract" /* Subtract */, defaultTooltip: "Subtract", icon: import_lucide.SquaresSubtract },
2231
+ { id: "drag" /* Drag */, defaultTooltip: "Drag", icon: import_lucide.Move },
2232
+ { id: "scale" /* Scale */, defaultTooltip: "Scale", icon: import_lucide.Scaling },
2233
+ { id: "rotate" /* Rotate */, defaultTooltip: "Rotate", icon: import_lucide.RotateCw },
2234
+ { id: "edit" /* Edit */, defaultTooltip: "Edit", icon: import_lucide.SquarePen },
2235
+ { id: "delete" /* Delete */, defaultTooltip: "Delete", icon: import_lucide.Trash2 },
2236
+ { id: "off" /* Off */, defaultTooltip: "Turn Off", icon: import_lucide.Power }
2237
2237
  ];
2238
+ var MODE_CONFIGS_BY_ID = new Map(MODE_CONFIGS.map((config) => [config.id, config]));
2238
2239
  function ensureToolbarStyles() {
2239
2240
  if (typeof document === "undefined" || document.getElementById(ANVIL_TOOLBAR_STYLE_ID)) return;
2240
2241
  const style = document.createElement("style");
@@ -2304,6 +2305,12 @@ function buildIcon(iconNode) {
2304
2305
  focusable: "false"
2305
2306
  });
2306
2307
  }
2308
+ function resolveModeTooltip(config, modeTooltips) {
2309
+ if (typeof modeTooltips === "function") {
2310
+ return modeTooltips(config.id, config.defaultTooltip) ?? config.defaultTooltip;
2311
+ }
2312
+ return modeTooltips?.[config.id] ?? config.defaultTooltip;
2313
+ }
2307
2314
  var AnvilControl = class extends L21.Control {
2308
2315
  constructor(anvil, options) {
2309
2316
  super(L21.Util.extend({ position: "topleft" }, options));
@@ -2321,11 +2328,16 @@ var AnvilControl = class extends L21.Control {
2321
2328
  const createButton = (group, config) => {
2322
2329
  const btn = L21.DomUtil.create("a", "anvil-control-btn", group);
2323
2330
  btn.href = "#";
2324
- btn.title = config.title;
2325
2331
  btn.setAttribute("role", "button");
2326
- btn.setAttribute("aria-label", config.title);
2327
2332
  btn.appendChild(buildIcon(config.icon));
2333
+ const syncTooltip = () => {
2334
+ const tooltip = resolveModeTooltip(config, this._options.modeTooltips);
2335
+ btn.title = tooltip;
2336
+ btn.setAttribute("aria-label", tooltip);
2337
+ };
2338
+ syncTooltip();
2328
2339
  L21.DomEvent.disableClickPropagation(btn);
2340
+ L21.DomEvent.on(btn, "mouseover focus", syncTooltip);
2329
2341
  L21.DomEvent.on(btn, "click", (e) => {
2330
2342
  L21.DomEvent.preventDefault(e);
2331
2343
  if (config.id === "off" /* Off */) {
@@ -2339,12 +2351,12 @@ var AnvilControl = class extends L21.Control {
2339
2351
  blocks.forEach((block, index) => {
2340
2352
  const group = L21.DomUtil.create("div", "leaflet-bar anvil-toolbar-group", container);
2341
2353
  block.forEach((modeId) => {
2342
- const config = MODE_CONFIGS.find(({ id }) => id === modeId);
2354
+ const config = MODE_CONFIGS_BY_ID.get(modeId);
2343
2355
  if (!config) return;
2344
2356
  createButton(group, config);
2345
2357
  });
2346
2358
  if (index === blocks.length - 1 && !this._btns["off" /* Off */]) {
2347
- createButton(group, MODE_CONFIGS.find(({ id }) => id === "off" /* Off */));
2359
+ createButton(group, MODE_CONFIGS_BY_ID.get("off" /* Off */));
2348
2360
  }
2349
2361
  });
2350
2362
  const updateFn = (mode) => {
@@ -2440,7 +2452,8 @@ var Anvil = class {
2440
2452
  });
2441
2453
  this.control = anvilControl(this, {
2442
2454
  position: this.options.controlPosition,
2443
- modes: modesFromOptions
2455
+ modes: modesFromOptions,
2456
+ modeTooltips: this.options.modeTooltips
2444
2457
  });
2445
2458
  this.control?.addTo(this.map);
2446
2459
  }
package/dist/index.mjs CHANGED
@@ -2200,25 +2200,26 @@ import {
2200
2200
  } from "lucide";
2201
2201
  var ANVIL_TOOLBAR_STYLE_ID = "anvil-toolbar-styles";
2202
2202
  var MODE_CONFIGS = [
2203
- { id: "marker" /* Marker */, title: "Marker", icon: MapPin },
2204
- { id: "polyline" /* Polyline */, title: "Line", icon: Waypoints },
2205
- { id: "polygon" /* Polygon */, title: "Polygon", icon: Pentagon },
2206
- { id: "rectangle" /* Rectangle */, title: "Rectangle", icon: RectangleHorizontal },
2207
- { id: "square" /* Square */, title: "Square", icon: Square },
2208
- { id: "triangle" /* Triangle */, title: "Triangle", icon: Triangle },
2209
- { id: "circle" /* Circle */, title: "Circle", icon: Circle6 },
2210
- { id: "freehand" /* Freehand */, title: "Freehand", icon: Hand },
2211
- { id: "cut" /* Cut */, title: "Cut", icon: Scissors },
2212
- { id: "split" /* Split */, title: "Split", icon: Split },
2213
- { id: "union" /* Union */, title: "Union", icon: SquaresUnite },
2214
- { id: "subtract" /* Subtract */, title: "Subtract", icon: SquaresSubtract },
2215
- { id: "drag" /* Drag */, title: "Drag", icon: Move },
2216
- { id: "scale" /* Scale */, title: "Scale", icon: Scaling },
2217
- { id: "rotate" /* Rotate */, title: "Rotate", icon: RotateCw },
2218
- { id: "edit" /* Edit */, title: "Edit", icon: SquarePen },
2219
- { id: "delete" /* Delete */, title: "Delete", icon: Trash2 },
2220
- { id: "off" /* Off */, title: "Turn Off", icon: Power }
2203
+ { id: "marker" /* Marker */, defaultTooltip: "Marker", icon: MapPin },
2204
+ { id: "polyline" /* Polyline */, defaultTooltip: "Line", icon: Waypoints },
2205
+ { id: "polygon" /* Polygon */, defaultTooltip: "Polygon", icon: Pentagon },
2206
+ { id: "rectangle" /* Rectangle */, defaultTooltip: "Rectangle", icon: RectangleHorizontal },
2207
+ { id: "square" /* Square */, defaultTooltip: "Square", icon: Square },
2208
+ { id: "triangle" /* Triangle */, defaultTooltip: "Triangle", icon: Triangle },
2209
+ { id: "circle" /* Circle */, defaultTooltip: "Circle", icon: Circle6 },
2210
+ { id: "freehand" /* Freehand */, defaultTooltip: "Freehand", icon: Hand },
2211
+ { id: "cut" /* Cut */, defaultTooltip: "Cut", icon: Scissors },
2212
+ { id: "split" /* Split */, defaultTooltip: "Split", icon: Split },
2213
+ { id: "union" /* Union */, defaultTooltip: "Union", icon: SquaresUnite },
2214
+ { id: "subtract" /* Subtract */, defaultTooltip: "Subtract", icon: SquaresSubtract },
2215
+ { id: "drag" /* Drag */, defaultTooltip: "Drag", icon: Move },
2216
+ { id: "scale" /* Scale */, defaultTooltip: "Scale", icon: Scaling },
2217
+ { id: "rotate" /* Rotate */, defaultTooltip: "Rotate", icon: RotateCw },
2218
+ { id: "edit" /* Edit */, defaultTooltip: "Edit", icon: SquarePen },
2219
+ { id: "delete" /* Delete */, defaultTooltip: "Delete", icon: Trash2 },
2220
+ { id: "off" /* Off */, defaultTooltip: "Turn Off", icon: Power }
2221
2221
  ];
2222
+ var MODE_CONFIGS_BY_ID = new Map(MODE_CONFIGS.map((config) => [config.id, config]));
2222
2223
  function ensureToolbarStyles() {
2223
2224
  if (typeof document === "undefined" || document.getElementById(ANVIL_TOOLBAR_STYLE_ID)) return;
2224
2225
  const style = document.createElement("style");
@@ -2288,6 +2289,12 @@ function buildIcon(iconNode) {
2288
2289
  focusable: "false"
2289
2290
  });
2290
2291
  }
2292
+ function resolveModeTooltip(config, modeTooltips) {
2293
+ if (typeof modeTooltips === "function") {
2294
+ return modeTooltips(config.id, config.defaultTooltip) ?? config.defaultTooltip;
2295
+ }
2296
+ return modeTooltips?.[config.id] ?? config.defaultTooltip;
2297
+ }
2291
2298
  var AnvilControl = class extends L21.Control {
2292
2299
  constructor(anvil, options) {
2293
2300
  super(L21.Util.extend({ position: "topleft" }, options));
@@ -2305,11 +2312,16 @@ var AnvilControl = class extends L21.Control {
2305
2312
  const createButton = (group, config) => {
2306
2313
  const btn = L21.DomUtil.create("a", "anvil-control-btn", group);
2307
2314
  btn.href = "#";
2308
- btn.title = config.title;
2309
2315
  btn.setAttribute("role", "button");
2310
- btn.setAttribute("aria-label", config.title);
2311
2316
  btn.appendChild(buildIcon(config.icon));
2317
+ const syncTooltip = () => {
2318
+ const tooltip = resolveModeTooltip(config, this._options.modeTooltips);
2319
+ btn.title = tooltip;
2320
+ btn.setAttribute("aria-label", tooltip);
2321
+ };
2322
+ syncTooltip();
2312
2323
  L21.DomEvent.disableClickPropagation(btn);
2324
+ L21.DomEvent.on(btn, "mouseover focus", syncTooltip);
2313
2325
  L21.DomEvent.on(btn, "click", (e) => {
2314
2326
  L21.DomEvent.preventDefault(e);
2315
2327
  if (config.id === "off" /* Off */) {
@@ -2323,12 +2335,12 @@ var AnvilControl = class extends L21.Control {
2323
2335
  blocks.forEach((block, index) => {
2324
2336
  const group = L21.DomUtil.create("div", "leaflet-bar anvil-toolbar-group", container);
2325
2337
  block.forEach((modeId) => {
2326
- const config = MODE_CONFIGS.find(({ id }) => id === modeId);
2338
+ const config = MODE_CONFIGS_BY_ID.get(modeId);
2327
2339
  if (!config) return;
2328
2340
  createButton(group, config);
2329
2341
  });
2330
2342
  if (index === blocks.length - 1 && !this._btns["off" /* Off */]) {
2331
- createButton(group, MODE_CONFIGS.find(({ id }) => id === "off" /* Off */));
2343
+ createButton(group, MODE_CONFIGS_BY_ID.get("off" /* Off */));
2332
2344
  }
2333
2345
  });
2334
2346
  const updateFn = (mode) => {
@@ -2424,7 +2436,8 @@ var Anvil = class {
2424
2436
  });
2425
2437
  this.control = anvilControl(this, {
2426
2438
  position: this.options.controlPosition,
2427
- modes: modesFromOptions
2439
+ modes: modesFromOptions,
2440
+ modeTooltips: this.options.modeTooltips
2428
2441
  });
2429
2442
  this.control?.addTo(this.map);
2430
2443
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "leaflet-anvil",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "description": "A minimal drawing and editing toolkit for Leaflet.",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.js",