@riebeckite/plugin-map 0.0.31 → 0.0.33

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
@@ -77,6 +77,17 @@ map:
77
77
  `lat`/`lng` also accept `latitude`/`longitude`, `coordinates: [lat, lng]` or
78
78
  `coordinates: "lat, lng"`, and `label` also accepts `title`/`place`/`name`.
79
79
 
80
+ ## Live example
81
+
82
+ ~~~map
83
+ center: 35.6812, 139.7671
84
+ zoom: 13
85
+ label: Tokyo Station
86
+ markers:
87
+ - 35.6812, 139.7671 | Tokyo Station
88
+ - 35.6586, 139.7454 | Tokyo Tower
89
+ ~~~
90
+
80
91
  ## How it renders
81
92
 
82
93
  A block or frontmatter map becomes a `figure.rr-map`:
@@ -156,7 +167,7 @@ to inject a fake Leaflet).
156
167
 
157
168
  ## Main exports
158
169
 
159
- - `map(options?)`: create the plugin (`mapPlugin` is an alias)
170
+ - `map(options?)`: create the plugin (`map` is an alias)
160
171
  - `initMap`: initialize client-side maps
161
172
  - `parseMapSource`: parse a fenced block into `MapData`
162
173
  - `normalizeMapInput`: normalize frontmatter into `MapData`
package/README_ja.md CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  [English](./README.md)
8
8
 
9
- ## 設定する
9
+ ## 使い方
10
10
 
11
11
  ```ts
12
12
  import { defineConfig } from "@riebeckite/core";
@@ -33,7 +33,7 @@ export default defineConfig({
33
33
 
34
34
  ### コードブロック
35
35
 
36
- ブロック本文は小さな key/value 形式です。`markers:` の後に `-` でマーカーを並べ、`lat, lng` だけの行は暗黙のマーカーになります。
36
+ ブロック本文は簡単なキーと値の形式です。`markers:` の後に `-` でマーカーを並べ、`lat, lng` だけの行は暗黙のマーカーになります。
37
37
 
38
38
  ````markdown
39
39
  ```map
@@ -70,7 +70,18 @@ map:
70
70
 
71
71
  `lat`/`lng` は `latitude`/`longitude`、`coordinates: [lat, lng]` または `coordinates: "lat, lng"` も受け付けます。`label` は `title`/`place`/`name` も使えます。
72
72
 
73
- ## どのように描画されるか
73
+ ## 実行例
74
+
75
+ ~~~map
76
+ center: 35.6812, 139.7671
77
+ zoom: 13
78
+ label: Tokyo Station
79
+ markers:
80
+ - 35.6812, 139.7671 | Tokyo Station
81
+ - 35.6586, 139.7454 | Tokyo Tower
82
+ ~~~
83
+
84
+ ## 描画の仕組み
74
85
 
75
86
  コードブロックまたは frontmatter の地図は `figure.rr-map` になります。
76
87
 
@@ -80,7 +91,7 @@ map:
80
91
  - `div.rr-map__static`: 常に利用できるフォールバック。`p.rr-map__place`、`ul.rr-map__markers` → `a.rr-map__link`(OpenStreetMap リンク)、任意の `img.rr-map__image`、`p.rr-map__attribution` を含みます
81
92
  - `details.rr-map__fallback`: 元のブロック本文を折りたたんで表示
82
93
 
83
- `initMap` は `[data-rr-map="pending"]` を探し、Leaflet を遅延して読み込みます。タイルレイヤーとマーカーを描画して `data-rr-map="rendered"` にします(このとき静的フォールバックは非表示になります)。失敗した場合は `data-rr-map="error"` になり、`details` を開き、静的フォールバックは表示されたままです。
94
+ `initMap` は `[data-rr-map="pending"]` を探し、Leaflet を遅延読み込みします。タイルレイヤーとマーカーを描画して `data-rr-map="rendered"` にします(このとき静的フォールバックは非表示になります)。失敗した場合は `data-rr-map="error"` になり、`details` を開き、静的フォールバックは表示されたままになります。
84
95
 
85
96
  座標も `center` もないブロックは置き換えず、通常のコードブロックのまま残し、`source: "@riebeckite/plugin-map"` を持つ診断を出します。
86
97
 
@@ -103,7 +114,7 @@ map:
103
114
 
104
115
  ## タイルと帰属表示
105
116
 
106
- 既定はキー不要の [OpenStreetMap 標準タイル](https://tile.openstreetmap.org/) です。小規模なサイトには十分ですが、OpenStreetMap の[タイル利用ポリシー](https://operations.osmfoundation.org/policies/tiles/)は。識別可能な referrer を前提とし、大量アクセスを禁止しています。一定のトラフィックがあるサイトは `tileUrl` を自前のタイルサーバーや商用プロバイダに向けてください。
117
+ 既定はキー不要の [OpenStreetMap 標準タイル](https://tile.openstreetmap.org/) です。小規模なサイトには十分ですが、OpenStreetMap の[タイル利用ポリシー](https://operations.osmfoundation.org/policies/tiles/)は、識別可能なリファラーを前提とし、一括取得や高負荷な利用を禁じています。一定のトラフィックがあるサイトは `tileUrl` を自前のタイルサーバーや商用プロバイダに向けてください。
107
118
 
108
119
  帰属表示は省略できません。`attribution` は Leaflet に渡され、静的フォールバックにも(プレーンテキストで)描画されます。プロバイダが要求するクレジットを常に表示してください。
109
120
 
@@ -111,11 +122,11 @@ map:
111
122
 
112
123
  ## クライアント側の描画
113
124
 
114
- `[data-rr-map="pending"]` の figure が 1 つ以上あるときだけ取得します。取得元は jsDelivr で、Leaflet(`leaflet.js` と `leaflet.css`)です。ライブラリを遅延して読み込むため、地図のないページは何も負担しません。地図のあるページでも、JavaScript を無効にしていたり CDN に到達できなかったりする場合があります。その場合は静的フォールバックが表示されます。
125
+ `[data-rr-map="pending"]` の figure が 1 つ以上あるときだけ取得します。取得元は jsDelivr の Leaflet(`leaflet.js` と `leaflet.css`)です。遅延読み込みなので、地図のないページに負担はかかりません。地図のあるページでも、JavaScript が無効だったり CDN に到達できなかったりすれば、静的フォールバックが表示されます。
115
126
 
116
- `initMap(options?)` が受け取るオプションは次のとおりです。`tileUrl`、`attribution`、`minZoom`、`maxZoom`、`leafletScriptUrl`、`leafletStyleUrl`。さらに、事前読み込み済みの `runtime` も指定できます(テストで偽の Leaflet を注入する用途)。
127
+ `initMap(options?)` が受け取るオプションは次のとおりです。`tileUrl`、`attribution`、`minZoom`、`maxZoom`、`leafletScriptUrl`、`leafletStyleUrl`。さらに、事前読み込み済みの `runtime` も指定できます(テストでモックの Leaflet を注入する用途)。
117
128
 
118
- ## 出力のフック
129
+ ## 出力フック
119
130
 
120
131
  - `figure[data-rr-map]`: 状態(`pending` / `rendered` / `error`)
121
132
  - `figure[data-rr-map-payload]`: 解決済みの地図データ JSON
@@ -125,7 +136,7 @@ map:
125
136
 
126
137
  ## 主なエクスポート
127
138
 
128
- - `map(options?)`: プラグインを作成する(`mapPlugin` は別名)
139
+ - `map(options?)`: プラグインを作成する(`map` は別名)
129
140
  - `initMap`: クライアント側の地図を初期化する
130
141
  - `parseMapSource`: コードブロックを `MapData` に変換する
131
142
  - `normalizeMapInput`: frontmatter を `MapData` に正規化する
package/dist/client.js CHANGED
@@ -4,19 +4,28 @@ var DEFAULT_LEAFLET_STYLE_URL = "https://cdn.jsdelivr.net/npm/leaflet@1.9.4/dist
4
4
  var DEFAULT_TILE_URL = "https://tile.openstreetmap.org/{z}/{x}/{y}.png";
5
5
  var DEFAULT_ATTRIBUTION = '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors';
6
6
  var leafletPromise = null;
7
- async function initMap(options = {}) {
7
+ async function initMap(context = {}) {
8
+ const options = context.config ?? {};
9
+ const root = context.root ?? document;
8
10
  const figures = Array.from(
9
- document.querySelectorAll('[data-rr-map="pending"]')
11
+ root.querySelectorAll('[data-rr-map="pending"]')
10
12
  );
11
13
  if (figures.length === 0) return;
12
14
  const runtime = options.runtime ?? await loadLeaflet(options);
15
+ if (context.signal?.aborted) return;
13
16
  if (!runtime) {
14
17
  for (const figure of figures) setError(figure);
15
18
  return;
16
19
  }
20
+ const maps = [];
17
21
  for (const figure of figures) {
18
- renderMap(runtime, figure, options);
22
+ if (context.signal?.aborted || !figure.isConnected) break;
23
+ const map = renderMap(runtime, figure, options);
24
+ if (map) maps.push(map);
19
25
  }
26
+ return () => {
27
+ for (const map of maps) map.remove();
28
+ };
20
29
  }
21
30
  function renderMap(runtime, figure, options) {
22
31
  const canvas = figure.querySelector("[data-rr-map-canvas]");
@@ -57,6 +66,7 @@ function renderMap(runtime, figure, options) {
57
66
  map.setView([payload.center.lat, payload.center.lng], payload.zoom);
58
67
  }
59
68
  figure.dataset.rrMap = "rendered";
69
+ return map;
60
70
  } catch {
61
71
  setError(figure);
62
72
  }
package/dist/index.d.ts CHANGED
@@ -13,5 +13,3 @@ export declare const MAP_PLUGIN_NAME = "map";
13
13
  * map is present and upgrades each figure to an interactive map.
14
14
  */
15
15
  export declare function map(options?: MapOptions): import("@riebeckite/core").RiebeckitePlugin<MapOptions>;
16
- /** Alias matching the `*Plugin` suffix used by other plugin factories. */
17
- export declare const mapPlugin: typeof map;
package/dist/index.js CHANGED
@@ -56,50 +56,20 @@ function clampZoom(value, minimum = 1) {
56
56
  }
57
57
 
58
58
  // src/hast.ts
59
- function visitElements(node, visitor, parent, index) {
60
- if (isElementNode(node)) visitor(node, parent, index);
61
- for (const [childIndex, child] of [...getChildren(node)].entries()) {
62
- visitElements(
63
- child,
64
- visitor,
65
- hasChildren(node) ? node : parent,
66
- childIndex
67
- );
68
- }
69
- }
70
- function element(tagName, properties = {}, children = []) {
71
- return { type: "element", tagName, properties, children };
72
- }
73
- function text(value) {
74
- return { type: "text", value };
75
- }
76
- function getTextContent(node) {
77
- if (node.type === "text" && "value" in node && typeof node.value === "string") {
78
- return node.value;
79
- }
80
- return getChildren(node).map(getTextContent).join("");
81
- }
82
- function getStringProperty(node, key) {
83
- const value = node.properties?.[key];
84
- if (typeof value === "string") return value;
85
- if (Array.isArray(value)) return value.join(" ");
86
- return null;
87
- }
88
- function hasClass(node, className) {
89
- const value = node.properties?.className;
90
- const classes = Array.isArray(value) ? value.map(String) : typeof value === "string" ? value.split(/\s+/) : [];
91
- return classes.includes(className);
92
- }
93
- function isElementNode(node) {
94
- return node.type === "element";
95
- }
96
- function hasChildren(node) {
97
- return Array.isArray(node.children);
98
- }
99
- function getChildren(node) {
100
- const children = node.children;
101
- return Array.isArray(children) ? children : [];
102
- }
59
+ import {
60
+ element as sharedElement,
61
+ getStringProperty as sharedGetStringProperty,
62
+ getTextContent as sharedGetTextContent,
63
+ hasClass as sharedHasClass,
64
+ text as sharedText,
65
+ visitElements as sharedVisitElements
66
+ } from "@riebeckite/diagram";
67
+ var element = sharedElement;
68
+ var text = sharedText;
69
+ var getTextContent = sharedGetTextContent;
70
+ var getStringProperty = sharedGetStringProperty;
71
+ var hasClass = sharedHasClass;
72
+ var visitElements = sharedVisitElements;
103
73
 
104
74
  // src/parse.ts
105
75
  var KEY_VALUE = /^([A-Za-z][\w-]*)\s*:\s*(.*)$/;
@@ -422,10 +392,14 @@ function buildFigure(input) {
422
392
  }
423
393
  if (options.fallback && input.source) {
424
394
  children.push(
425
- element("details", { className: `${options.className}__fallback` }, [
426
- element("summary", {}, [text("Map source")]),
427
- element("pre", {}, [element("code", {}, [text(input.source)])])
428
- ])
395
+ element(
396
+ "details",
397
+ { className: `${options.className}__fallback rb-source-fallback` },
398
+ [
399
+ element("summary", {}, [text("Map source")]),
400
+ element("pre", {}, [element("code", {}, [text(input.source)])])
401
+ ]
402
+ )
429
403
  );
430
404
  }
431
405
  return element(
@@ -578,12 +552,13 @@ function map(options = {}) {
578
552
  attribution: resolved.attribution,
579
553
  minZoom: resolved.minZoom,
580
554
  maxZoom: resolved.maxZoom
581
- }
555
+ },
556
+ void 0,
557
+ "context"
582
558
  )
583
559
  ]
584
560
  });
585
561
  }
586
- var mapPlugin = map;
587
562
  function validateMapOptions(options) {
588
563
  if (!options) return [];
589
564
  const issues = [];
@@ -630,7 +605,6 @@ export {
630
605
  describeMap,
631
606
  formatCoordinates,
632
607
  map,
633
- mapPlugin,
634
608
  normalizeMapInput,
635
609
  openStreetMapUrl,
636
610
  parseMapSource,
@@ -1,7 +1,7 @@
1
1
  import type { ElementNode, HastNode, ParentNode, TextNode } from "./types.js";
2
- export declare function visitElements(node: HastNode, visitor: (node: ElementNode, parent?: ParentNode, index?: number) => void, parent?: ParentNode, index?: number): void;
3
- export declare function element(tagName: string, properties?: Record<string, unknown>, children?: HastNode[]): ElementNode;
4
- export declare function text(value: string): TextNode;
5
- export declare function getTextContent(node: HastNode): string;
6
- export declare function getStringProperty(node: ElementNode, key: string): string | null;
7
- export declare function hasClass(node: ElementNode, className: string): boolean;
2
+ export declare const element: (tagName: string, properties?: Record<string, unknown>, children?: HastNode[]) => ElementNode;
3
+ export declare const text: (value: string) => TextNode;
4
+ export declare const getTextContent: (node: HastNode) => string;
5
+ export declare const getStringProperty: (node: ElementNode, key: string) => string | null;
6
+ export declare const hasClass: (node: ElementNode, className: string) => boolean;
7
+ export declare const visitElements: (node: HastNode, visitor: (node: ElementNode, parent?: ParentNode, index?: number) => void, parent?: ParentNode, index?: number) => void;
@@ -1,4 +1,9 @@
1
1
  import type { MapClientOptions } from "./types.js";
2
+ type ClientContext = {
3
+ root?: ParentNode;
4
+ signal?: AbortSignal;
5
+ config?: MapClientOptions;
6
+ };
2
7
  /**
3
8
  * Upgrades every server-emitted `[data-rr-map="pending"]` figure to an
4
9
  * interactive Leaflet map.
@@ -8,4 +13,5 @@ import type { MapClientOptions } from "./types.js";
8
13
  * `data-rr-map-payload`, which means the page still renders its static
9
14
  * fallback when JavaScript is disabled or the CDN is unreachable.
10
15
  */
11
- export declare function initMap(options?: MapClientOptions): Promise<void>;
16
+ export declare function initMap(context?: ClientContext): Promise<undefined | (() => void)>;
17
+ export {};
@@ -1,26 +1,4 @@
1
1
  import type { MapOptions, ResolvedMapOptions } from "./types.js";
2
- /** Default base class for the generated figure. */
3
- export declare const DEFAULT_MAP_CLASS_NAME = "rr-map";
4
- /** Default map height in pixels. */
5
- export declare const DEFAULT_MAP_HEIGHT = 320;
6
- /** Default fenced-code language. */
7
- export declare const DEFAULT_MAP_LANGUAGE = "map";
8
- /** Default zoom level. */
9
- export declare const DEFAULT_MAP_ZOOM = 13;
10
- /** Default minimum zoom level. */
11
- export declare const DEFAULT_MAP_MIN_ZOOM = 1;
12
- /** Default maximum zoom level (OpenStreetMap's standard maximum). */
13
- export declare const DEFAULT_MAP_MAX_ZOOM = 19;
14
- /** Frontmatter property read for coordinates by default. */
15
- export declare const DEFAULT_MAP_FRONTMATTER_KEY = "map";
16
- /**
17
- * Default OpenStreetMap tile template. It is keyless and safe to expose to the
18
- * browser, but sites with meaningful traffic should follow the tile usage
19
- * policy or point `tileUrl` at their own tile server.
20
- */
21
- export declare const DEFAULT_MAP_TILE_URL = "https://tile.openstreetmap.org/{z}/{x}/{y}.png";
22
- /** Attribution required by the default tile provider. */
23
- export declare const DEFAULT_MAP_ATTRIBUTION = "&copy; <a href=\"https://www.openstreetmap.org/copyright\">OpenStreetMap</a> contributors";
24
2
  /**
25
3
  * Applies defaults to the user-provided options.
26
4
  *
@@ -121,11 +121,11 @@ export type MapClientOptions = {
121
121
  runtime?: MapRuntime;
122
122
  };
123
123
  /** The subset of the Leaflet API the initializer uses. */
124
- export type LeafletLayer = {
124
+ type LeafletLayer = {
125
125
  addTo(map: LeafletMap): LeafletLayer;
126
126
  bindPopup?(content: string): LeafletLayer;
127
127
  };
128
- export type LeafletMap = {
128
+ type LeafletMap = {
129
129
  setView(center: [number, number], zoom: number): LeafletMap;
130
130
  fitBounds(bounds: unknown, options?: Record<string, unknown>): LeafletMap;
131
131
  remove(): void;
@@ -149,7 +149,7 @@ export type TextNode = {
149
149
  type: "text";
150
150
  value: string;
151
151
  };
152
- export type RawNode = {
152
+ type RawNode = {
153
153
  type: "raw";
154
154
  value: string;
155
155
  };
@@ -157,3 +157,4 @@ export type HastNode = ElementNode | TextNode | RawNode | {
157
157
  type: string;
158
158
  [key: string]: unknown;
159
159
  };
160
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@riebeckite/plugin-map",
3
- "version": "0.0.31",
3
+ "version": "0.0.33",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -21,7 +21,8 @@
21
21
  "./style.css": "./style.css"
22
22
  },
23
23
  "dependencies": {
24
- "@riebeckite/core": "0.0.31"
24
+ "@riebeckite/core": "0.0.33",
25
+ "@riebeckite/diagram": "0.0.33"
25
26
  },
26
27
  "publishConfig": {
27
28
  "access": "public"
package/style.css CHANGED
@@ -75,26 +75,6 @@
75
75
  font-size: 0.75rem;
76
76
  }
77
77
 
78
- .rr-map__fallback {
79
- border-top: 1px solid var(--rr-map-rule);
80
- color: var(--rr-map-muted);
81
- font-size: 0.8125rem;
82
- }
83
-
84
- .rr-map__fallback summary {
85
- cursor: pointer;
86
- padding: 0.5rem 0.75rem;
87
- }
88
-
89
- .rr-map__fallback pre {
90
- overflow: auto;
91
- margin: 0 1rem;
92
- padding: 0.75rem;
93
- border-top: 1px solid var(--rr-map-rule);
94
- color: var(--rr-map-ink);
95
- background: color-mix(in srgb, var(--rr-map-ink) 6%, transparent);
96
- }
97
-
98
78
  .rr-map[data-rr-map="error"] .rr-map__static::before {
99
79
  content: "The interactive map could not be loaded. ";
100
80
  color: var(--rr-map-muted);