@tileflow/react 0.1.0-alpha.24 → 0.1.0-alpha.26

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.
Files changed (3) hide show
  1. package/README.md +192 -225
  2. package/dist/index.js +15 -35
  3. package/package.json +4 -4
package/README.md CHANGED
@@ -1,86 +1,153 @@
1
1
  # @tileflow/react
2
2
 
3
- React components for rendering Tileflow maps with MapLibre.
3
+ React components for interactive Tileflow maps, annotations, and static-image display.
4
+ Interactive maps use MapLibre GL JS; browser components read prepared assets rather than compiling
5
+ `tileflow.config.ts`.
4
6
 
5
- Install `maplibre-gl` alongside the package and import its CSS once in your app.
7
+ > Related packages and guides: [documentation index](https://raw.githubusercontent.com/tileflow/tileflow-sdk/main/llms.txt).
8
+
9
+ ## Install
10
+
11
+ Use an existing React 18 or 19 application with matching `react-dom`. The following local build
12
+ workflow needs Node.js 22 or newer:
13
+
14
+ ```sh
15
+ npm install @tileflow/react@alpha @tileflow/core@alpha @tileflow/maps@alpha "maplibre-gl@^6.4.1"
16
+ npm install --save-dev --save-exact tileflow@alpha
17
+ ```
18
+
19
+ The supported peer ranges are React/React DOM 18–19 and MapLibre GL JS `>=6.4.1 <7`. The worker
20
+ must come from the same MapLibre installation as the main module.
21
+
22
+ ## Prepare a map
23
+
24
+ Create `tileflow.config.ts` at the application root:
25
+
26
+ <!-- docs:check -->
27
+
28
+ ```ts
29
+ import {defineMap} from '@tileflow/core';
30
+ import {streets} from '@tileflow/maps';
31
+
32
+ export default defineMap({
33
+ id: 'madrid',
34
+ version: 1,
35
+ extends: streets,
36
+ view: {center: [-3.7038, 40.4168], zoom: 12},
37
+ });
38
+ ```
39
+
40
+ Build the assets into the application's public directory:
41
+
42
+ ```sh
43
+ npx tileflow build --out public/tileflow
44
+ ```
45
+
46
+ This assumes the application serves `public/` at its URL root. Rebuild after config or asset
47
+ changes, or use the [Vite](https://github.com/tileflow/tileflow-sdk/blob/main/packages/vite/README.md),
48
+ [Next.js](https://github.com/tileflow/tileflow-sdk/blob/main/packages/next/README.md), or
49
+ [Webpack](https://github.com/tileflow/tileflow-sdk/blob/main/packages/webpack/README.md) integration
50
+ for watched development and production builds. Do not use both workflows to overwrite the same
51
+ hosted manifest.
52
+
53
+ ## Configure the worker
54
+
55
+ In a Vite application's client entry, add this before mounting React:
56
+
57
+ ```ts
58
+ import workerUrl from 'maplibre-gl/dist/maplibre-gl-worker.mjs?worker&url';
59
+ import {configureTileflowMapLibre} from '@tileflow/react';
60
+
61
+ configureTileflowMapLibre({workerUrl});
62
+ ```
63
+
64
+ This import syntax is specific to Vite. Use the Next.js or Webpack guide linked above for that
65
+ bundler's worker-delivery recipe. Configure the worker once, outside component rendering. Do not
66
+ use a CDN worker from another MapLibre version. Next.js interactive components belong behind a
67
+ `'use client'` boundary.
68
+
69
+ ## Render a map
70
+
71
+ Import MapLibre's CSS once in the application. After the worker and assets are ready, render:
72
+
73
+ <!-- docs:check -->
6
74
 
7
75
  ```tsx
8
- import {Map} from '@tileflow/react';
9
76
  import 'maplibre-gl/dist/maplibre-gl.css';
77
+ import {Map} from '@tileflow/react';
10
78
 
11
79
  export function App() {
12
80
  return (
13
81
  <Map
14
- source={{kind: 'tileflow', map: 'madrid'}}
82
+ source={{map: 'madrid'}}
15
83
  theme="system"
16
84
  center={[-3.7038, 40.4168]}
17
85
  zoom={12}
18
- mapOptions={{
19
- cooperativeGestures: true,
20
- maxZoom: 18,
21
- }}
86
+ mapOptions={{cooperativeGestures: true, maxZoom: 18}}
22
87
  />
23
88
  );
24
89
  }
25
90
  ```
26
91
 
27
- ## MapLibre 6 worker
92
+ Coordinates are `[longitude, latitude]`. `source` is one object, `{map, manifestUrl?}`.
93
+ `source.map` must match the config's portable map ID, not a hosted `map_...` identifier.
94
+ The default manifest URL is exactly `/tileflow/manifest.json`. For a subpath or external host,
95
+ provide `manifestUrl` in the source; the component does not infer bundler configuration or search
96
+ for assets.
28
97
 
29
- With MapLibre GL JS 6, configure the application-owned worker once before mounting an interactive
30
- map. The worker URL must come from the same `maplibre-gl` package installed by the application.
98
+ `theme` selects a published concrete theme. Omission uses `defaultTheme`; `system` requires an
99
+ explicit light/dark mapping, which Streets supplies. Theme changes preserve the MapLibre instance,
100
+ camera, and interactions, and roll back on failure. `onThemeChange` reports transitions.
31
101
 
32
- ```tsx
33
- import {configureTileflowMapLibre} from '@tileflow/react';
34
- import workerUrl from 'maplibre-gl/dist/maplibre-gl-worker.mjs?worker&url';
102
+ `mapOptions` accepts native MapLibre options except `container` and `style`. Direct camera props
103
+ take priority over `mapOptions`, then the manifest view, then shared defaults. `Map` always selects
104
+ a Tileflow map through its manifest; it has no renderer discriminator or direct-style mode.
105
+ The obsolete source fields `kind` and `style` are rejected. For a completely unmanaged map, use
106
+ `maplibre-gl` directly rather than a Tileflow component.
35
107
 
36
- configureTileflowMapLibre({workerUrl});
37
- ```
108
+ ## Add annotations and popups
38
109
 
39
- This is the Vite setup. Call it in the client entry before rendering `Map`; `mode="image"` remains
40
- MapLibre-free. MapLibre GL JS 6.4.1-6.x is supported. Use the `@tileflow/next` or
41
- `@tileflow/webpack` README for their worker-delivery recipes.
110
+ Install the contract package when importing its types directly:
42
111
 
43
- ## Annotations, tooltips, and popups
112
+ ```sh
113
+ npm install @tileflow/interactions@alpha
114
+ ```
115
+
116
+ Annotations are small, application-owned marker sets. Every annotation has a stable `id`, singular
117
+ `coordinate`, and non-empty `ariaLabel`. Data must be JSON-safe. Native React renderers use portals
118
+ into hosts managed by the interaction runtime:
44
119
 
45
- Use `annotations` for small application-owned marker sets. Annotation data is serializable, while
46
- `renderMarker`, `renderTooltip`, and `renderPopup` render native React UI through portals into the
47
- MapLibre-owned marker and overlay hosts.
120
+ <!-- docs:check -->
48
121
 
49
122
  ```tsx
50
- import {Map} from '@tileflow/react';
51
123
  import {useState} from 'react';
52
124
  import type {TileflowAnnotation, TileflowInteractionState} from '@tileflow/interactions';
125
+ import {Map} from '@tileflow/react';
53
126
 
54
- type Property = {
55
- price: number;
56
- title: string;
57
- };
127
+ type Property = {price: number; title: string};
58
128
 
59
129
  const annotations = [
60
130
  {
61
- ariaLabel: 'Apartment in Madrid',
62
- coordinate: [-3.7038, 40.4168],
63
- data: {price: 320_000, title: 'Apartment in Madrid'},
64
131
  id: 'property-42',
65
132
  kind: 'marker',
66
- marker: {content: {kind: 'field', field: 'price'}},
133
+ coordinate: [-3.7038, 40.4168],
134
+ ariaLabel: 'Apartment in Madrid',
135
+ data: {price: 320_000, title: 'Apartment in Madrid'},
67
136
  tooltip: {content: {kind: 'field', field: 'title'}},
68
137
  popup: {content: {kind: 'view', name: 'property-card'}},
69
138
  },
70
139
  ] satisfies readonly TileflowAnnotation<Property>[];
71
140
 
72
141
  export function PropertyMap() {
73
- const [interactionState, setInteractionState] = useState<TileflowInteractionState>({popup: null});
142
+ const [state, setState] = useState<TileflowInteractionState>({popup: null});
74
143
 
75
144
  return (
76
145
  <Map
77
- source={{kind: 'tileflow', map: 'madrid'}}
146
+ source={{map: 'madrid'}}
78
147
  annotations={annotations}
79
- interactionState={interactionState}
80
- onInteractionStateChange={setInteractionState}
81
- onInteractionDiagnostic={(diagnostic) => console.error(diagnostic)}
82
- onInteractionEvent={(event) => console.log(event.type, event.target)}
83
- renderMarker={({annotation}) => <strong>EUR {annotation.data.price.toLocaleString()}</strong>}
148
+ interactionState={state}
149
+ onInteractionStateChange={setState}
150
+ onInteractionDiagnostic={(diagnostic) => console.error(diagnostic.code)}
84
151
  renderPopup={({annotation, close}) => (
85
152
  <article>
86
153
  <h2>{annotation.data.title}</h2>
@@ -95,208 +162,108 @@ export function PropertyMap() {
95
162
  }
96
163
  ```
97
164
 
98
- Omit `interactionState` and use `defaultInteractionState` for uncontrolled popup state. Callback and
99
- renderer identity changes, annotation updates, and popup state changes do not recreate the MapLibre
100
- map. Updating an annotation with the same ID preserves its compatible Marker instance and portal
101
- host.
165
+ This uses the worker/CSS setup from the preceding sections. Omit controlled `interactionState` and
166
+ use `defaultInteractionState` for uncontrolled state; never supply both or switch ownership during
167
+ the component's lifetime. Updating annotations, renderers, callbacks, or state does not recreate
168
+ the map. Reusing an annotation ID preserves a compatible marker host.
102
169
 
103
- `renderMarker` is always annotation-only. Without `interactions`, `renderTooltip` and `renderPopup`
104
- also infer the annotation-specific context, including typed `annotation.data`. When `interactions`
105
- is present, they receive the common `TileflowInteractionViewContext`; narrow `context.target.kind`
106
- (or check `annotation in context`) when one renderer serves both annotation and semantic targets.
170
+ Use `interactions` with `target: {kind: 'semantic-feature', domain: 'poi'}` for POIs already in the
171
+ compiled map instead of creating a DOM marker for every feature. Optional categories use Tileflow's
172
+ semantic taxonomy, such as `food-drink` or `retail`, not raw source classes. The style must contain
173
+ compatible interaction metadata. With semantic bindings, narrow `context.target.kind` in
174
+ `renderTooltip` or `renderPopup` before accessing annotation or feature data. `renderMarker` remains
175
+ annotation-only. See the
176
+ [interaction guide](https://github.com/tileflow/tileflow-sdk/blob/main/packages/interactions/README.md).
107
177
 
108
- ## Semantic POI interactions
178
+ Text/field content uses safe text defaults. A `view` is an application renderer name, not HTML.
179
+ Keep tooltips non-interactive; put buttons, links, and forms in popups.
109
180
 
110
- Use `interactions` to attach tooltip or popup behavior to POIs already rendered by a finalized
111
- Tileflow style. Application code names the `poi` domain, never compiler-generated MapLibre layer
112
- IDs. The runtime queries only manifest-declared layers and mounts DOM only for the active overlay.
181
+ ## Display an image or create a render
113
182
 
114
- ```tsx
115
- import {Map} from '@tileflow/react';
116
- import type {TileflowInteractionBinding} from '@tileflow/interactions';
117
-
118
- const interactions = [
119
- {
120
- id: 'poi-details',
121
- target: {kind: 'semantic-feature', domain: 'poi', categories: ['food-drink', 'retail']},
122
- tooltip: {content: {kind: 'field', field: 'name', fallback: 'Point of interest'}},
123
- popup: {content: {kind: 'view', name: 'poi-card'}},
124
- },
125
- ] satisfies readonly TileflowInteractionBinding[];
126
-
127
- export function PoiMap() {
128
- return (
129
- <Map
130
- source={{kind: 'tileflow', map: 'madrid'}}
131
- interactions={interactions}
132
- renderPopup={(context) =>
133
- context.target.kind === 'semantic-feature' ? (
134
- <article>
135
- <h2>{String(context.target.feature.properties.name ?? 'Point of interest')}</h2>
136
- <button type="button" onClick={context.close}>
137
- Close
138
- </button>
139
- </article>
140
- ) : null
141
- }
142
- />
143
- );
144
- }
145
- ```
146
-
147
- Bindings and popup state are validated before reconciliation. An invalid replacement leaves the
148
- last valid runtime state active and reports structured diagnostics. `interactionState` and
149
- `defaultInteractionState` are mutually exclusive. Annotation and semantic overlays share one popup
150
- state, so replacing either target closes the previous popup before opening the next.
151
-
152
- `MapProps` rejects controlled and default interaction state together. Its `mode="image"` branch
153
- excludes annotations, semantic bindings, interaction state, renderers, and interaction callbacks.
154
- JavaScript callers and widened inputs still receive the corresponding runtime diagnostics.
155
-
156
- `onInteractionDiagnostic` receives each newly reported structured runtime diagnostic. Changing any
157
- callback identity does not resubscribe the runtime or recreate MapLibre.
158
-
159
- Custom portal targets move capture readiness to `loading` until React commits the target and two
160
- current animation frames complete. Removing or replacing a target invalidates pending frames.
161
-
162
- When no render prop is present, text and safe field descriptors use the shared unbranded DOM
163
- defaults. A `view` descriptor is a dispatch name for an application renderer; it is not HTML or a
164
- serialized component tree. Tooltip content remains non-interactive, while popup content may contain
165
- normal React controls.
166
-
167
- `annotations` is the only application-owned marker input. Its singular `coordinate`, required
168
- `ariaLabel`, and optional `marker` descriptor feed the keyed runtime directly. `mode="image"`
169
- cannot render interaction configuration and shows an explicit `UNSUPPORTED_MODE` diagnostic
170
- without loading MapLibre.
171
-
172
- `mapOptions` accepts native MapLibre options except `container` and `style`, which
173
- Tileflow resolves from `source`. Direct
174
- Tileflow props such as `center`, `zoom`, and `interactive` take priority when
175
- provided. Camera resolution is the same in interactive and image modes: direct props, then
176
- `mapOptions`, then the published manifest view, then Tileflow's shared runtime defaults.
177
-
178
- Every map has exactly one discriminated `source`. Use `kind: 'tileflow'` for a map published in a
179
- Tileflow manifest. `kind: 'maplibre'` is the unmanaged MapLibre escape hatch for one direct style
180
- object or URL: it deliberately has no Tileflow theme identity, `system` selection, switching, or
181
- manifest traceability. Browser components do not compile `tileflow.config.ts`.
182
-
183
- Without `manifestUrl`, the exact default is `/tileflow/manifest.json`. If Vite, Webpack, Next, a
184
- reverse proxy, or the Tileflow plugin publishes it anywhere else, set the final public URL
185
- explicitly; the component does not attempt runtime base-path discovery. Manifest 404s, unknown map
186
- IDs, unresolved styles, and unresolved image URLs enter `data-tileflow-state="error"`.
187
-
188
- `theme` selects a published theme name. Omitting it uses the map's `defaultTheme`; `"system"` is
189
- available only when the map declares explicit light and dark mappings. Changes preload fonts, diff
190
- the style on the existing MapLibre instance, preserve camera and interactions, and roll back if the
191
- new style fails. `onThemeChange` reports preloading, applying, ready, and error transitions.
192
-
193
- To reuse one published map across multiple repos, point every app at the same
194
- manifest:
183
+ `<Map mode="image">` displays an explicit `imageUrl` or derives an image URL from the selected
184
+ manifest map's `mapId`, `apiUrl`, concrete theme and view. It still requires a valid Tileflow source.
185
+ It does not create a static render, poll an operation, or load MapLibre. A local style build alone
186
+ does not create that image. Annotations, semantic bindings, and interaction state are not supported
187
+ in image mode.
195
188
 
196
- ```tsx
197
- <Map
198
- source={{
199
- kind: 'tileflow',
200
- map: 'madrid',
201
- manifestUrl: 'https://cdn.example.com/tileflow/manifest.json',
202
- }}
203
- />
204
- ```
205
-
206
- Or deliberately leave the Tileflow theme contract and load one concrete Style JSON directly:
189
+ To request a new static render, use `StaticMap` from `@tileflow/react/static`. First install the
190
+ shared client/types used by this example:
207
191
 
208
- ```tsx
209
- <Map
210
- source={{
211
- kind: 'maplibre',
212
- style: 'https://cdn.example.com/tileflow/styles/madrid/dark.json',
213
- }}
214
- />
192
+ ```sh
193
+ npm install @tileflow/static@alpha
215
194
  ```
216
195
 
217
- For a lightweight display of an already hosted static image, use image mode:
196
+ The application must implement an authorized `/api/static-maps` endpoint that forwards the normalized
197
+ scene and idempotency key to the hosted service and returns its result contract. Keep privileged
198
+ Tileflow credentials on that server, not in React props or a browser bundle.
218
199
 
219
- ```tsx
220
- <Map
221
- source={{kind: 'tileflow', map: 'madrid'}}
222
- mode="image"
223
- center={[-3.7038, 40.4168]}
224
- zoom={12}
225
- />
226
- ```
227
-
228
- `Map mode="image"` only resolves an existing image URL (explicitly or from the published map
229
- manifest) and renders an `<img>`; it does not submit a render scene, create an operation, poll, or
230
- apply `StaticMap` overlays. This path does not load or evaluate MapLibre. Without an explicit
231
- `imageUrl`, all environments use the image URL published for the Tileflow source.
232
-
233
- For a new, fully specified render with exact size, center/bounds camera, overlays, idempotency, and
234
- asynchronous create/poll behavior, use `StaticMap` from `@tileflow/react/static` or helpers from
235
- `@tileflow/static`.
200
+ <!-- docs:check -->
236
201
 
237
202
  ```tsx
238
- import {StaticMap} from '@tileflow/react/static';
239
203
  import {useState} from 'react';
240
- import {createStaticMapIdempotencyKey} from '@tileflow/static';
204
+ import {StaticMap} from '@tileflow/react/static';
205
+ import type {StaticMapAttributionEntry} from '@tileflow/static';
241
206
 
242
- export function Preview() {
243
- const [idempotencyKey] = useState(createStaticMapIdempotencyKey);
207
+ export function StaticPreview({idempotencyKey}: {idempotencyKey: string}) {
208
+ const [entries, setEntries] = useState<readonly StaticMapAttributionEntry[]>([]);
244
209
 
245
210
  return (
246
- <StaticMap
247
- map="madrid"
248
- camera={{type: 'center', center: [-3.7038, 40.4168], zoom: 12}}
249
- attribution={{mode: 'external'}}
250
- size={{width: 1200, height: 800}}
251
- createUrl="/api/static-maps"
252
- idempotencyKey={idempotencyKey}
253
- onReady={(result) => {
254
- if (result.resultVersion === 2) showAttribution(result.attribution.entries);
255
- }}
256
- />
211
+ <figure>
212
+ <StaticMap
213
+ map="madrid"
214
+ theme="dark"
215
+ alt="Map of central Madrid"
216
+ camera={{type: 'center', center: [-3.7038, 40.4168], zoom: 12}}
217
+ size={{width: 1200, height: 800}}
218
+ attribution={{mode: 'external'}}
219
+ createUrl="/api/static-maps"
220
+ idempotencyKey={idempotencyKey}
221
+ onReady={(result) => {
222
+ if ('attribution' in result) setEntries(result.attribution.entries);
223
+ }}
224
+ />
225
+ <figcaption>
226
+ {entries.map((entry, index) => (
227
+ <p key={index}>
228
+ {entry.text}{' '}
229
+ {entry.links.map((link) => (
230
+ <a key={link.url} href={link.url}>
231
+ {link.label}
232
+ </a>
233
+ ))}
234
+ </p>
235
+ ))}
236
+ </figcaption>
237
+ </figure>
257
238
  );
258
239
  }
259
240
  ```
260
241
 
261
- `idempotencyKey` is required in create mode. Keep one key for one intentional create action and
262
- reuse it across retries and remounts. Concurrent React consumers share work only when URL,
263
- normalized scene, and idempotency key all identify the same operation; unmounting the final
264
- consumer aborts its in-flight request.
265
-
266
- `StaticMap` forwards `attribution` as part of the normalized scene and request identity. Omission
267
- uses embedded automatic placement. With `mode: 'external'`, the component still renders the image,
268
- but the application must render every structured `onReady` attribution entry beside it. Existing
269
- `imageUrl` mode has no operation result and cannot infer attribution from an immutable image URL.
270
-
271
- Hosted interactive maps automatically preflight a short-lived commercial session grant before
272
- eligible resources. Setting `analytics={{enabled: false}}` disables the optional beacon only; it
273
- does not remove hosted authorization or override a user `mapOptions.transformRequest` callback.
274
- Use `analytics={{surfaceId: 'store-locator'}}` when the same Map is embedded in several stable
275
- product locations. Missing or invalid Surface IDs become `default`; do not use URLs, branches,
276
- random instance IDs, or user IDs.
277
- Direct Tileflow World maps keep early `GRACE` silent, show a compact accessible owner-action pill
278
- when late `GRACE` is activated, and show a stronger banner in `MANAGED_REQUIRED`. Missing tiles and
279
- MapLibre failures do not remove that recovery path.
280
-
281
- ## Headless capture readiness
282
-
283
- Use `captureId` to disambiguate multiple maps with the same configured name:
284
-
285
- ```tsx
286
- <Map source={{kind: 'tileflow', map: 'madrid'}} captureId="checkout-map" />
287
- ```
288
-
289
- The root element exposes `data-tileflow-map`, optional `data-tileflow-capture-id`, and
290
- `data-tileflow-state="loading|idle|error"`. Interactive mode becomes idle after MapLibre is idle
291
- and two animation frames; style/data work returns it to loading. Image mode waits for decode or a
292
- successful load fallback, including cached hydration. `tileflow capture` uses these markers for
293
- application scenes and requires exactly one target.
294
-
295
- ## Compatibility
296
-
297
- The supported peer window is React 18-19 and MapLibre GL JS 6.4.1-6.x. Compatibility smoke tests
298
- install the exact lower bound and the selected current release from packed Tileflow tarballs,
299
- typecheck a consumer, and render the image/SSR path while rejecting any MapLibre import. Future
300
- majors stay outside the peer range until that matrix passes.
301
-
302
- Docs: https://tileflow.dev/docs
242
+ Create the key with `createStaticMapIdempotencyKey()` once per intentional render action and retain
243
+ it in the owning application state across retries and remounts. Pass it into this component; do not
244
+ regenerate it on every render. A new scene is a new action and needs a new key.
245
+
246
+ Hosted results have `resultVersion: 1` and attribution. `onReady` also supports existing-image
247
+ results, which have no attribution field; narrow the union before reading it. External attribution
248
+ must remain visible beside the image. Omitted attribution uses embedded automatic placement.
249
+ See the [Static Maps guide](https://github.com/tileflow/tileflow-sdk/blob/main/packages/static/README.md)
250
+ for server transport, errors, overlays, and limits.
251
+
252
+ ## Capture, authorization, and errors
253
+
254
+ `captureId` disambiguates repeated maps on a page. The root exposes `data-tileflow-map`, the resolved
255
+ `data-tileflow-theme`, optional `data-tileflow-capture-id`, and `data-tileflow-state="loading|idle|error"`.
256
+ Capture waits for MapLibre/image readiness and committed custom interaction views. Manifest 404s,
257
+ unknown maps/themes, and missing styles or images enter the error state rather than silently using
258
+ another map.
259
+
260
+ Hosted interactive maps acquire a short-lived session grant before eligible resource requests.
261
+ `analytics={{enabled: false}}` disables the optional beacon, not hosted authorization. A stable
262
+ `surfaceId`, such as `store-locator`, identifies a product location; do not use user IDs or URLs.
263
+ Existing request transforms remain application-owned, and direct World use can display an
264
+ owner-action notice when managed delivery is required.
265
+
266
+ The image/SSR path does not load MapLibre. Interactive rendering needs a browser with WebGL and the
267
+ matching worker assets. For release-specific APIs, use the installed declarations and README;
268
+ source `main` may be ahead of npm. Report defects in the
269
+ [issue tracker](https://github.com/tileflow/tileflow-sdk/issues).
package/dist/index.js CHANGED
@@ -37,8 +37,7 @@ import {
37
37
  resolveTileflowRuntimeStyle,
38
38
  resolveTileflowRuntimeTheme,
39
39
  resolveTileflowRuntimeView,
40
- resolveTileflowStaticImageUrl,
41
- shouldLoadTileflowManifest
40
+ resolveTileflowStaticImageUrl
42
41
  } from "@tileflow/core/runtime";
43
42
  import {
44
43
  createTileflowMapLibreDomRuntime,
@@ -175,9 +174,6 @@ function validateTileflowMapStyleInputs(input) {
175
174
  if (input.theme !== void 0 && !validateTileflowThemeSelection(input.theme)) {
176
175
  return { error: 'theme must be a concrete portable theme name or "system"', ok: false };
177
176
  }
178
- if (input.theme !== void 0 && input.source.kind !== "tileflow") {
179
- return { error: "theme is only valid with a tileflow source", ok: false };
180
- }
181
177
  return { ok: true };
182
178
  }
183
179
  function assertTileflowMapStyleInputs(input) {
@@ -253,16 +249,14 @@ function Map2(props) {
253
249
  } = props;
254
250
  const generalRenderPopup = renderPopup;
255
251
  const generalRenderTooltip = renderTooltip;
256
- const sourceKind = source.kind;
257
- const sourceMap = source.kind === "tileflow" ? source.map : void 0;
258
- const sourceManifestUrl = source.kind === "tileflow" ? source.manifestUrl : void 0;
259
- const sourceStyle = source.kind === "maplibre" ? source.style : void 0;
252
+ const sourceMap = source.map;
253
+ const sourceManifestUrl = source.manifestUrl;
260
254
  const runtimeSource = useMemo(
261
- () => sourceKind === "tileflow" ? { kind: "tileflow", manifestUrl: sourceManifestUrl, map: sourceMap } : { kind: "maplibre", style: sourceStyle },
262
- [sourceKind, sourceManifestUrl, sourceMap, sourceStyle]
255
+ () => ({ manifestUrl: sourceManifestUrl, map: sourceMap }),
256
+ [sourceManifestUrl, sourceMap]
263
257
  );
264
- const mapName = runtimeSource.kind === "tileflow" ? runtimeSource.map : void 0;
265
- const manifestUrl = runtimeSource.kind === "tileflow" ? runtimeSource.manifestUrl ?? defaultTileflowManifestUrl : defaultTileflowManifestUrl;
258
+ const mapName = runtimeSource.map;
259
+ const manifestUrl = runtimeSource.manifestUrl ?? defaultTileflowManifestUrl;
266
260
  const preparedAnnotations = useMemo(
267
261
  () => prepareTileflowReactAnnotations(annotations),
268
262
  [annotations]
@@ -328,22 +322,12 @@ function Map2(props) {
328
322
  const resolvedCaptureId = normalizeTileflowCaptureId(captureId);
329
323
  const resolvedMode = resolveTileflowMapMode({ mode });
330
324
  const isImageMode = resolvedMode === "image";
331
- const shouldLoadManifest = shouldLoadTileflowManifest({
332
- source: runtimeSource
333
- });
334
- const manifestRequestKey = shouldLoadManifest ? JSON.stringify([manifestUrl, mapName]) : "not-needed";
335
- const currentManifestResolution = manifestResolution.key === manifestRequestKey ? manifestResolution : {
336
- key: manifestRequestKey,
337
- map: null,
338
- state: shouldLoadManifest ? "loading" : "not-needed"
339
- };
325
+ const manifestRequestKey = JSON.stringify([manifestUrl, mapName]);
326
+ const currentManifestResolution = manifestResolution.key === manifestRequestKey ? manifestResolution : { key: manifestRequestKey, map: null, state: "loading" };
340
327
  const manifestMap = currentManifestResolution.state === "ready" ? currentManifestResolution.map : null;
341
328
  const themeResolution = useMemo(() => {
342
- if (!manifestMap || runtimeSource.kind !== "tileflow") {
343
- return {
344
- error: false,
345
- name: runtimeSource.kind === "tileflow" && theme && theme !== "system" ? theme : void 0
346
- };
329
+ if (!manifestMap) {
330
+ return { error: false, name: theme && theme !== "system" ? theme : void 0 };
347
331
  }
348
332
  try {
349
333
  return {
@@ -353,7 +337,7 @@ function Map2(props) {
353
337
  } catch {
354
338
  return { error: true, name: void 0 };
355
339
  }
356
- }, [manifestMap, runtimeSource, systemColorScheme, theme]);
340
+ }, [manifestMap, systemColorScheme, theme]);
357
341
  const resolvedThemeName = themeResolution.name;
358
342
  const manifestView = resolveTileflowRuntimeView({ manifestMap });
359
343
  const manifestCenter = useStableMapOptionValue(
@@ -458,10 +442,6 @@ function Map2(props) {
458
442
  reportedDeclarativeDiagnosticKeysRef.current = nextKeys;
459
443
  }, [declarativeInteractionDiagnostics]);
460
444
  useEffect(() => {
461
- if (!shouldLoadManifest) {
462
- setManifestResolution({ key: manifestRequestKey, map: null, state: "not-needed" });
463
- return;
464
- }
465
445
  let cancelled = false;
466
446
  setManifestResolution({ key: manifestRequestKey, map: null, state: "loading" });
467
447
  loadTileflowManifest(manifestUrl).then((manifest) => {
@@ -483,7 +463,7 @@ function Map2(props) {
483
463
  return () => {
484
464
  cancelled = true;
485
465
  };
486
- }, [manifestRequestKey, manifestUrl, mapName, shouldLoadManifest]);
466
+ }, [manifestRequestKey, manifestUrl, mapName]);
487
467
  const runtimeStyle = useMemo(() => {
488
468
  if (isImageMode || themeResolution.error) {
489
469
  return null;
@@ -496,7 +476,7 @@ function Map2(props) {
496
476
  });
497
477
  }, [isImageMode, manifestMap, runtimeSource, systemColorScheme, themeResolution]);
498
478
  runtimeStyleRef.current = runtimeStyle;
499
- const runtimeMapIdentity = runtimeSource.kind === "tileflow" ? `${manifestUrl}\0${runtimeSource.map}` : runtimeSource.style;
479
+ const runtimeMapIdentity = `${manifestUrl}\0${runtimeSource.map}`;
500
480
  const runtimeStyleReady = runtimeStyle !== null || themeResolution.error && mapRef.current !== null;
501
481
  const resolvedAnalytics = useMemo(
502
482
  () => mergeTileflowAnalytics(analytics, runtimeStyle?.analytics),
@@ -544,7 +524,7 @@ function Map2(props) {
544
524
  ]
545
525
  );
546
526
  const runtimeResolutionState = (() => {
547
- if (shouldLoadManifest && currentManifestResolution.state !== "ready") {
527
+ if (currentManifestResolution.state !== "ready") {
548
528
  return currentManifestResolution.state === "error" ? "error" : "loading";
549
529
  }
550
530
  if (themeResolution.error) return "error";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tileflow/react",
3
- "version": "0.1.0-alpha.24",
3
+ "version": "0.1.0-alpha.26",
4
4
  "license": "Apache-2.0",
5
5
  "description": "React components for rendering Tileflow maps with MapLibre.",
6
6
  "type": "module",
@@ -43,9 +43,9 @@
43
43
  "access": "public"
44
44
  },
45
45
  "dependencies": {
46
- "@tileflow/core": ">=0.1.0-alpha.24 <0.1.0-beta.0",
47
- "@tileflow/interactions": ">=0.1.0-alpha.3 <0.1.0-beta.0",
48
- "@tileflow/static": ">=0.1.0-alpha.24 <0.1.0-beta.0"
46
+ "@tileflow/core": ">=0.1.0-alpha.27 <0.1.0-beta.0",
47
+ "@tileflow/interactions": ">=0.1.0-alpha.4 <0.1.0-beta.0",
48
+ "@tileflow/static": ">=0.1.0-alpha.27 <0.1.0-beta.0"
49
49
  },
50
50
  "peerDependencies": {
51
51
  "maplibre-gl": ">=6.4.1 <7",