@react-x11/components 0.4.0 → 0.5.0

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 (219) hide show
  1. package/README.md +125 -4
  2. package/dist/charts/components.d.ts +3 -1
  3. package/dist/charts/components.d.ts.map +1 -1
  4. package/dist/charts/components.js +8 -4
  5. package/dist/charts/components.js.map +1 -1
  6. package/dist/charts/node.d.ts +13 -6
  7. package/dist/charts/node.d.ts.map +1 -1
  8. package/dist/charts/node.js +75 -24
  9. package/dist/charts/node.js.map +1 -1
  10. package/dist/charts/render.d.ts +4 -0
  11. package/dist/charts/render.d.ts.map +1 -1
  12. package/dist/charts/render.js +11 -6
  13. package/dist/charts/render.js.map +1 -1
  14. package/dist/code-editor/node.d.ts +47 -16
  15. package/dist/code-editor/node.d.ts.map +1 -1
  16. package/dist/code-editor/node.js +121 -51
  17. package/dist/code-editor/node.js.map +1 -1
  18. package/dist/flow/draw.d.ts +19 -1
  19. package/dist/flow/draw.d.ts.map +1 -1
  20. package/dist/flow/draw.js +64 -35
  21. package/dist/flow/draw.js.map +1 -1
  22. package/dist/flow/index.d.ts.map +1 -1
  23. package/dist/flow/index.js +24 -8
  24. package/dist/flow/index.js.map +1 -1
  25. package/dist/flow/model.d.ts +4 -2
  26. package/dist/flow/model.d.ts.map +1 -1
  27. package/dist/flow/model.js +4 -2
  28. package/dist/flow/model.js.map +1 -1
  29. package/dist/flow/node.d.ts +40 -9
  30. package/dist/flow/node.d.ts.map +1 -1
  31. package/dist/flow/node.js +142 -53
  32. package/dist/flow/node.js.map +1 -1
  33. package/dist/flow/types.d.ts +46 -21
  34. package/dist/flow/types.d.ts.map +1 -1
  35. package/dist/formula/index.d.ts +4 -2
  36. package/dist/formula/index.d.ts.map +1 -1
  37. package/dist/formula/index.js.map +1 -1
  38. package/dist/formula/node.d.ts +5 -1
  39. package/dist/formula/node.d.ts.map +1 -1
  40. package/dist/formula/node.js +19 -3
  41. package/dist/formula/node.js.map +1 -1
  42. package/dist/html/controls.d.ts +4 -1
  43. package/dist/html/controls.d.ts.map +1 -1
  44. package/dist/html/controls.js.map +1 -1
  45. package/dist/html/css/cascade.d.ts +11 -5
  46. package/dist/html/css/cascade.d.ts.map +1 -1
  47. package/dist/html/css/cascade.js +18 -8
  48. package/dist/html/css/cascade.js.map +1 -1
  49. package/dist/html/css/parse.d.ts +15 -10
  50. package/dist/html/css/parse.d.ts.map +1 -1
  51. package/dist/html/css/parse.js +41 -16
  52. package/dist/html/css/parse.js.map +1 -1
  53. package/dist/html/css/style.d.ts +3 -0
  54. package/dist/html/css/style.d.ts.map +1 -1
  55. package/dist/html/css/style.js +3 -1
  56. package/dist/html/css/style.js.map +1 -1
  57. package/dist/html/css/values.d.ts +14 -1
  58. package/dist/html/css/values.d.ts.map +1 -1
  59. package/dist/html/css/values.js +11 -8
  60. package/dist/html/css/values.js.map +1 -1
  61. package/dist/html/index.d.ts.map +1 -1
  62. package/dist/html/index.js +1 -0
  63. package/dist/html/index.js.map +1 -1
  64. package/dist/html/layout/boxes.d.ts +12 -5
  65. package/dist/html/layout/boxes.d.ts.map +1 -1
  66. package/dist/html/layout/boxes.js +7 -4
  67. package/dist/html/layout/boxes.js.map +1 -1
  68. package/dist/html/layout/inline.d.ts.map +1 -1
  69. package/dist/html/layout/inline.js +22 -1
  70. package/dist/html/layout/inline.js.map +1 -1
  71. package/dist/html/node.d.ts +22 -8
  72. package/dist/html/node.d.ts.map +1 -1
  73. package/dist/html/node.js +97 -15
  74. package/dist/html/node.js.map +1 -1
  75. package/dist/html/paint.d.ts +3 -0
  76. package/dist/html/paint.d.ts.map +1 -1
  77. package/dist/html/paint.js +2 -2
  78. package/dist/html/paint.js.map +1 -1
  79. package/dist/index.d.ts +7 -1
  80. package/dist/index.d.ts.map +1 -1
  81. package/dist/index.js +21 -0
  82. package/dist/index.js.map +1 -1
  83. package/dist/maps/clip.d.ts +43 -0
  84. package/dist/maps/clip.d.ts.map +1 -0
  85. package/dist/maps/clip.js +171 -0
  86. package/dist/maps/clip.js.map +1 -0
  87. package/dist/maps/gzip.d.ts +15 -0
  88. package/dist/maps/gzip.d.ts.map +1 -0
  89. package/dist/maps/gzip.js +56 -0
  90. package/dist/maps/gzip.js.map +1 -0
  91. package/dist/maps/index.d.ts +55 -0
  92. package/dist/maps/index.d.ts.map +1 -0
  93. package/dist/maps/index.js +141 -0
  94. package/dist/maps/index.js.map +1 -0
  95. package/dist/maps/labels.d.ts +123 -0
  96. package/dist/maps/labels.d.ts.map +1 -0
  97. package/dist/maps/labels.js +323 -0
  98. package/dist/maps/labels.js.map +1 -0
  99. package/dist/maps/mvt.d.ts +244 -0
  100. package/dist/maps/mvt.d.ts.map +1 -0
  101. package/dist/maps/mvt.js +820 -0
  102. package/dist/maps/mvt.js.map +1 -0
  103. package/dist/maps/node.d.ts +278 -0
  104. package/dist/maps/node.d.ts.map +1 -0
  105. package/dist/maps/node.js +1459 -0
  106. package/dist/maps/node.js.map +1 -0
  107. package/dist/maps/overlay.d.ts +157 -0
  108. package/dist/maps/overlay.d.ts.map +1 -0
  109. package/dist/maps/overlay.js +472 -0
  110. package/dist/maps/overlay.js.map +1 -0
  111. package/dist/maps/paint.d.ts +226 -0
  112. package/dist/maps/paint.d.ts.map +1 -0
  113. package/dist/maps/paint.js +705 -0
  114. package/dist/maps/paint.js.map +1 -0
  115. package/dist/maps/proj.d.ts +281 -0
  116. package/dist/maps/proj.d.ts.map +1 -0
  117. package/dist/maps/proj.js +451 -0
  118. package/dist/maps/proj.js.map +1 -0
  119. package/dist/maps/sources.d.ts +256 -0
  120. package/dist/maps/sources.d.ts.map +1 -0
  121. package/dist/maps/sources.js +210 -0
  122. package/dist/maps/sources.js.map +1 -0
  123. package/dist/maps/style.d.ts +141 -0
  124. package/dist/maps/style.d.ts.map +1 -0
  125. package/dist/maps/style.js +147 -0
  126. package/dist/maps/style.js.map +1 -0
  127. package/dist/maps/styles.d.ts +82 -0
  128. package/dist/maps/styles.d.ts.map +1 -0
  129. package/dist/maps/styles.js +898 -0
  130. package/dist/maps/styles.js.map +1 -0
  131. package/dist/maps/tiles.d.ts +283 -0
  132. package/dist/maps/tiles.d.ts.map +1 -0
  133. package/dist/maps/tiles.js +469 -0
  134. package/dist/maps/tiles.js.map +1 -0
  135. package/dist/maps/types.d.ts +244 -0
  136. package/dist/maps/types.d.ts.map +1 -0
  137. package/dist/maps/types.js +2 -0
  138. package/dist/maps/types.js.map +1 -0
  139. package/dist/reorder/hx.d.ts +18 -0
  140. package/dist/reorder/hx.d.ts.map +1 -0
  141. package/dist/reorder/hx.js +10 -0
  142. package/dist/reorder/hx.js.map +1 -0
  143. package/dist/reorder/index.d.ts +314 -0
  144. package/dist/reorder/index.d.ts.map +1 -0
  145. package/dist/reorder/index.js +1539 -0
  146. package/dist/reorder/index.js.map +1 -0
  147. package/dist/reorder/model.d.ts +107 -0
  148. package/dist/reorder/model.d.ts.map +1 -0
  149. package/dist/reorder/model.js +162 -0
  150. package/dist/reorder/model.js.map +1 -0
  151. package/dist/richtext/node.d.ts +15 -5
  152. package/dist/richtext/node.d.ts.map +1 -1
  153. package/dist/richtext/node.js +40 -11
  154. package/dist/richtext/node.js.map +1 -1
  155. package/dist/richtext/runs.d.ts +35 -13
  156. package/dist/richtext/runs.d.ts.map +1 -1
  157. package/dist/richtext/runs.js +83 -27
  158. package/dist/richtext/runs.js.map +1 -1
  159. package/dist/tabs/index.d.ts +17 -0
  160. package/dist/tabs/index.d.ts.map +1 -1
  161. package/dist/tabs/index.js +849 -186
  162. package/dist/tabs/index.js.map +1 -1
  163. package/dist/terminal/vt/fonts.d.ts +45 -15
  164. package/dist/terminal/vt/fonts.d.ts.map +1 -1
  165. package/dist/terminal/vt/fonts.js +89 -19
  166. package/dist/terminal/vt/fonts.js.map +1 -1
  167. package/dist/terminal/vt/node.d.ts +42 -0
  168. package/dist/terminal/vt/node.d.ts.map +1 -1
  169. package/dist/terminal/vt/node.js +95 -11
  170. package/dist/terminal/vt/node.js.map +1 -1
  171. package/dist/terminal/vt/renderer.d.ts.map +1 -1
  172. package/dist/terminal/vt/renderer.js +13 -3
  173. package/dist/terminal/vt/renderer.js.map +1 -1
  174. package/package.json +13 -3
  175. package/src/charts/components.ts +14 -7
  176. package/src/charts/node.ts +81 -26
  177. package/src/charts/render.ts +16 -6
  178. package/src/code-editor/node.ts +147 -53
  179. package/src/flow/draw.ts +104 -35
  180. package/src/flow/index.ts +33 -13
  181. package/src/flow/model.ts +4 -2
  182. package/src/flow/node.ts +158 -59
  183. package/src/flow/types.ts +46 -21
  184. package/src/formula/index.ts +4 -2
  185. package/src/formula/node.ts +24 -5
  186. package/src/html/controls.ts +4 -1
  187. package/src/html/css/cascade.ts +18 -7
  188. package/src/html/css/parse.ts +46 -21
  189. package/src/html/css/style.ts +6 -1
  190. package/src/html/css/values.ts +25 -9
  191. package/src/html/index.ts +1 -0
  192. package/src/html/layout/boxes.ts +19 -9
  193. package/src/html/layout/inline.ts +23 -1
  194. package/src/html/node.ts +110 -17
  195. package/src/html/paint.ts +17 -2
  196. package/src/index.ts +98 -0
  197. package/src/maps/clip.ts +184 -0
  198. package/src/maps/gzip.ts +66 -0
  199. package/src/maps/index.ts +316 -0
  200. package/src/maps/labels.ts +442 -0
  201. package/src/maps/mvt.ts +857 -0
  202. package/src/maps/node.ts +1819 -0
  203. package/src/maps/overlay.ts +639 -0
  204. package/src/maps/paint.ts +978 -0
  205. package/src/maps/proj.ts +641 -0
  206. package/src/maps/sources.ts +425 -0
  207. package/src/maps/style.ts +309 -0
  208. package/src/maps/styles.ts +1040 -0
  209. package/src/maps/tiles.ts +676 -0
  210. package/src/maps/types.ts +260 -0
  211. package/src/reorder/hx.ts +31 -0
  212. package/src/reorder/index.ts +2223 -0
  213. package/src/reorder/model.ts +232 -0
  214. package/src/richtext/node.ts +49 -14
  215. package/src/richtext/runs.ts +115 -30
  216. package/src/tabs/index.ts +1040 -204
  217. package/src/terminal/vt/fonts.ts +128 -36
  218. package/src/terminal/vt/node.ts +105 -12
  219. package/src/terminal/vt/renderer.ts +13 -3
@@ -0,0 +1,309 @@
1
+ // What to draw a tile's layers as.
2
+ //
3
+ // A **subset of the Mapbox/MapLibre GL style specification**, and a subset
4
+ // on purpose. The reason to shape it after that spec rather than invent one
5
+ // is that every provider documents their schema in its terms — Shortbread,
6
+ // OpenMapTiles, Mapbox Streets, Azure Maps, TomTom Orbis all publish "which
7
+ // layer, which field, at which zoom" as GL style fragments — so an author
8
+ // who has read any of those pages already knows what goes here.
9
+ //
10
+ // What is deliberately *not* here is the expression language. GL styles have
11
+ // grown a small typed Lisp (`['case', ['>', ['get', 'x'], 2], …]`) that is
12
+ // a real interpreter with a real cost per feature, and none of it is needed
13
+ // to say "roads, motorways thicker, at zoom 8 and up". So this takes the
14
+ // two older, simpler halves of the same spec:
15
+ //
16
+ // - **the legacy filter syntax** — `['all', ['==', 'kind', 'motorway'], …]`
17
+ // — which is a documented part of the spec, is what most schema
18
+ // documentation still shows, and compiles to a closure in one pass; and
19
+ // - **`stops`** for anything that varies with zoom, which is the older
20
+ // spelling of `['interpolate', ['linear'], ['zoom'], …]`.
21
+ //
22
+ // Both are resolved *once per layer per frame*, never per feature. That is
23
+ // the whole performance argument for the restriction: a dense tile has
24
+ // twelve thousand features and eighteen layers, so anything computed per
25
+ // layer is free and anything computed per feature is the frame.
26
+ import type { FeatureValue } from './mvt.js';
27
+ import { GeomType } from './mvt.js';
28
+ import type { FeatureCursor } from './mvt.js';
29
+
30
+ /**
31
+ * A value that may vary with the zoom.
32
+ *
33
+ * A bare value is constant. `{ stops }` is a list of `[zoom, value]` pairs,
34
+ * ordered by zoom: numbers interpolate linearly between the surrounding
35
+ * pair, and anything else steps at each stop. Outside the range the nearest
36
+ * stop holds, which is what makes a two-stop ramp a sensible whole answer
37
+ * rather than something that vanishes at zoom 0.
38
+ */
39
+ export type Zoomed<T> = T | { stops: readonly (readonly [number, T])[] };
40
+
41
+ /** Mapbox's legacy filter syntax, which is the part of it a style here
42
+ * speaks. Compiled with {@link compileFilter}. */
43
+ export type MapFilter =
44
+ | readonly ['==' | '!=', string, FeatureValue]
45
+ | readonly ['<' | '<=' | '>' | '>=', string, number]
46
+ | readonly ['in' | '!in', string, ...FeatureValue[]]
47
+ | readonly ['has' | '!has', string]
48
+ | readonly ['all' | 'any' | 'none', ...MapFilter[]]
49
+ // `$type` is the spec's name for the geometry type, and the one filter
50
+ // that does not read a tag.
51
+ | readonly ['geometry', 'point' | 'line' | 'polygon'];
52
+
53
+ interface LayerBase {
54
+ /** This layer's name in the style. Used in a `<Map>` event payload and as
55
+ * the cache key for anything derived from it. */
56
+ id: string;
57
+ /** Which source, when the map has more than one. The first source by
58
+ * default. */
59
+ source?: string;
60
+ /** Which layer of the tile — `'streets'`, `'water_polygons'`. */
61
+ sourceLayer: string;
62
+ /** The zoom range this layer draws in. A layer outside its range costs
63
+ * nothing: it is skipped before its features are touched. */
64
+ minZoom?: number;
65
+ maxZoom?: number;
66
+ filter?: MapFilter;
67
+ /** `false` keeps the layer in the style and stops drawing it — what a
68
+ * layer toggle in an application's UI switches. */
69
+ visible?: boolean;
70
+ }
71
+
72
+ /** An area. */
73
+ export interface FillLayer extends LayerBase {
74
+ type: 'fill';
75
+ color: Zoomed<string>;
76
+ opacity?: Zoomed<number>;
77
+ /** Stroked at one logical pixel around each ring, after the fill. */
78
+ outlineColor?: Zoomed<string>;
79
+ }
80
+
81
+ /** A line: a road, a river, a boundary, a route. */
82
+ export interface LineLayer extends LayerBase {
83
+ type: 'line';
84
+ color: Zoomed<string>;
85
+ /** Logical pixels. */
86
+ width: Zoomed<number>;
87
+ opacity?: Zoomed<number>;
88
+ /** A dash pattern in logical pixels. */
89
+ dash?: readonly number[];
90
+ cap?: 'butt' | 'round' | 'square';
91
+ join?: 'miter' | 'round' | 'bevel';
92
+ }
93
+
94
+ /** A point, as a disc. */
95
+ export interface CircleLayer extends LayerBase {
96
+ type: 'circle';
97
+ radius: Zoomed<number>;
98
+ color: Zoomed<string>;
99
+ opacity?: Zoomed<number>;
100
+ strokeColor?: Zoomed<string>;
101
+ strokeWidth?: Zoomed<number>;
102
+ }
103
+
104
+ /**
105
+ * A label.
106
+ *
107
+ * Symbol layers are **not** rasterized into the tile: they are collected as
108
+ * candidates, placed against every other label on screen, and drawn into
109
+ * the frame directly. Three reasons, and each of them is a bug if it is
110
+ * done the other way. Collision is global — two labels in different tiles
111
+ * overlap just as readily as two in one — so a per-tile placement produces
112
+ * a map that reads as clutter at every seam. A label baked into a tile
113
+ * surface is stretched by the fractional zoom, and text is the one thing on
114
+ * a map nobody accepts blurred. And a tile's labels would be clipped at its
115
+ * edge, which is exactly where half of them sit.
116
+ */
117
+ export interface SymbolLayer extends LayerBase {
118
+ type: 'symbol';
119
+ /** The tag to read the text from — `'name'`, or `'name:en'`. */
120
+ textField: string;
121
+ /** Logical pixels. */
122
+ textSize?: Zoomed<number>;
123
+ textColor?: Zoomed<string>;
124
+ /** Drawn under the glyphs, so a place name stays readable over a road. */
125
+ textHaloColor?: Zoomed<string>;
126
+ textHaloWidth?: Zoomed<number>;
127
+ /**
128
+ * Which label wins a collision, layer-wide. Higher wins; 0 by default,
129
+ * and the style's layer order breaks a tie.
130
+ *
131
+ * Deliberately not a per-feature expression. A schema's own importance
132
+ * field looks like the obvious input and is not usable as one: Shortbread
133
+ * carries `population`, which is 8,000,000 for London and 500 for Soho,
134
+ * so adding it to a rank that also has to order against street names
135
+ * needs a normalization nobody can guess. Splitting by `filter` — a layer
136
+ * for cities, a layer for suburbs — is exact, is what the ordering
137
+ * actually wants, and costs one more entry in a list.
138
+ */
139
+ rank?: number;
140
+ /**
141
+ * How far apart, in pixels, the same text may repeat within this layer.
142
+ * 250 by default; `0` lets it repeat as often as it fits.
143
+ *
144
+ * Not a nicety. A street is one feature **per segment** in every schema
145
+ * there is, so `street_labels` offers "Oxford Street" a dozen times over
146
+ * for one street, and a placement that only tests overlap accepts all
147
+ * twelve — they do not overlap, they are strung out along the road. The
148
+ * same is true of an administrative area whose name arrives once per
149
+ * polygon part. Repeating a long street's name across a wide view is
150
+ * wanted; repeating it four times in a hundred pixels is not, and the
151
+ * distance is where that line goes.
152
+ */
153
+ repeatDistance?: number;
154
+ }
155
+
156
+ export type MapStyleLayer = FillLayer | LineLayer | CircleLayer | SymbolLayer;
157
+
158
+ /** A whole style. */
159
+ export interface MapStyle {
160
+ /** Painted under every tile — the colour of the parts of the world that
161
+ * have no data yet, so it is what a map looks like while it loads. */
162
+ background?: string;
163
+ layers: readonly MapStyleLayer[];
164
+ /** The face labels are set in. The theme's `fontFamily` by default. */
165
+ fontFamily?: string;
166
+ }
167
+
168
+ // --- zoom ramps ------------------------------------------------------------
169
+
170
+ function isStops<T>(
171
+ value: Zoomed<T>,
172
+ ): value is { stops: readonly (readonly [number, T])[] } {
173
+ return (
174
+ typeof value === 'object' &&
175
+ value !== null &&
176
+ Array.isArray((value as { stops?: unknown }).stops)
177
+ );
178
+ }
179
+
180
+ /** A {@link Zoomed} at one zoom. Numbers interpolate, everything else
181
+ * steps. */
182
+ export function resolveZoomed<T>(value: Zoomed<T>, zoom: number): T {
183
+ if (!isStops(value)) return value;
184
+ const stops = value.stops;
185
+ if (stops.length === 0) throw new Error('a zoom ramp needs a stop');
186
+ if (zoom <= stops[0][0]) return stops[0][1];
187
+ const last = stops[stops.length - 1];
188
+ if (zoom >= last[0]) return last[1];
189
+ let i = 1;
190
+ while (i < stops.length && stops[i][0] <= zoom) i++;
191
+ const [z0, v0] = stops[i - 1];
192
+ const [z1, v1] = stops[i];
193
+ if (typeof v0 !== 'number' || typeof v1 !== 'number') return v0;
194
+ const t = z1 === z0 ? 0 : (zoom - z0) / (z1 - z0);
195
+ return (v0 + (v1 - v0) * t) as unknown as T;
196
+ }
197
+
198
+ // --- filters ---------------------------------------------------------------
199
+
200
+ /** A compiled filter: the shape the rasterizer calls per feature. */
201
+ export type CompiledFilter = (feature: FeatureCursor) => boolean;
202
+
203
+ const ALWAYS: CompiledFilter = () => true;
204
+
205
+ /**
206
+ * Compile a filter to a closure, once.
207
+ *
208
+ * The point of compiling rather than interpreting: the operator dispatch,
209
+ * the `in`-set construction and the geometry-type mapping all happen here,
210
+ * per layer, instead of twelve thousand times per tile. A `['in', 'kind',
211
+ * …]` with forty values becomes a `Set` lookup rather than a scan.
212
+ */
213
+ export function compileFilter(filter: MapFilter | undefined): CompiledFilter {
214
+ if (!filter) return ALWAYS;
215
+ const op = filter[0];
216
+ switch (op) {
217
+ case 'all': {
218
+ const parts = (filter.slice(1) as MapFilter[]).map(compileFilter);
219
+ if (parts.length === 0) return ALWAYS;
220
+ if (parts.length === 1) return parts[0];
221
+ return (f) => {
222
+ for (const part of parts) if (!part(f)) return false;
223
+ return true;
224
+ };
225
+ }
226
+ case 'any': {
227
+ const parts = (filter.slice(1) as MapFilter[]).map(compileFilter);
228
+ if (parts.length === 0) return ALWAYS;
229
+ return (f) => {
230
+ for (const part of parts) if (part(f)) return true;
231
+ return false;
232
+ };
233
+ }
234
+ case 'none': {
235
+ const parts = (filter.slice(1) as MapFilter[]).map(compileFilter);
236
+ return (f) => {
237
+ for (const part of parts) if (part(f)) return false;
238
+ return true;
239
+ };
240
+ }
241
+ case 'has': {
242
+ const key = filter[1];
243
+ return (f) => f.get(key) !== undefined;
244
+ }
245
+ case '!has': {
246
+ const key = filter[1];
247
+ return (f) => f.get(key) === undefined;
248
+ }
249
+ case '==': {
250
+ const [, key, want] = filter;
251
+ return (f) => f.get(key) === want;
252
+ }
253
+ case '!=': {
254
+ const [, key, want] = filter;
255
+ return (f) => f.get(key) !== want;
256
+ }
257
+ case '<':
258
+ case '<=':
259
+ case '>':
260
+ case '>=': {
261
+ const [, key, want] = filter;
262
+ return (f) => {
263
+ const value = f.get(key);
264
+ // A missing tag compares false rather than as zero: `['>' , 'pop',
265
+ // 0]` must not select every feature that has no population.
266
+ if (typeof value !== 'number') return false;
267
+ return op === '<'
268
+ ? value < want
269
+ : op === '<='
270
+ ? value <= want
271
+ : op === '>'
272
+ ? value > want
273
+ : value >= want;
274
+ };
275
+ }
276
+ case 'in': {
277
+ const key = filter[1];
278
+ const set = new Set(filter.slice(2) as FeatureValue[]);
279
+ return (f) => {
280
+ const value = f.get(key);
281
+ return value !== undefined && set.has(value);
282
+ };
283
+ }
284
+ case '!in': {
285
+ const key = filter[1];
286
+ const set = new Set(filter.slice(2) as FeatureValue[]);
287
+ return (f) => {
288
+ const value = f.get(key);
289
+ return value === undefined || !set.has(value);
290
+ };
291
+ }
292
+ case 'geometry': {
293
+ const want =
294
+ filter[1] === 'point'
295
+ ? GeomType.Point
296
+ : filter[1] === 'line'
297
+ ? GeomType.LineString
298
+ : GeomType.Polygon;
299
+ return (f) => f.type === want;
300
+ }
301
+ default: {
302
+ // An unknown operator draws nothing rather than everything: a typo in
303
+ // a filter that quietly selected the whole layer would look like the
304
+ // style working.
305
+ const unknown: never = op;
306
+ throw new Error(`unknown map filter operator ${String(unknown)}`);
307
+ }
308
+ }
309
+ }