@dnax/ui 0.0.48 → 0.0.50

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 (48) hide show
  1. package/components/QBottomSheet.vue +172 -6
  2. package/components/QChart.vue +1 -0
  3. package/components/QDatePicker.vue +1 -1
  4. package/components/QImagePicker.vue +28 -1
  5. package/components/QInputChat.vue +859 -0
  6. package/components/QInputCurrency.vue +319 -0
  7. package/components/QItemLabel.vue +99 -0
  8. package/components/QMap.vue +410 -0
  9. package/components/QNumericKeyboard.vue +304 -0
  10. package/components/QPage.vue +26 -6
  11. package/components/QPageContainer.vue +32 -0
  12. package/components/QPopupProxy.vue +378 -0
  13. package/components/QQrcode.vue +132 -0
  14. package/components/QSelect.vue +12 -0
  15. package/components/QSpreadsheet.vue +395 -69
  16. package/components/QTime.vue +845 -0
  17. package/components/QTimeline.vue +111 -0
  18. package/components/QTimelineEntry.vue +271 -0
  19. package/index.ts +11 -1
  20. package/lib/bottomSheet.test.ts +121 -0
  21. package/lib/bottomSheet.ts +115 -0
  22. package/lib/color.ts +13 -0
  23. package/lib/currency.test.ts +193 -0
  24. package/lib/currency.ts +301 -0
  25. package/lib/fixedLayout.ts +12 -11
  26. package/lib/icons.ts +3 -20
  27. package/lib/map.test.ts +365 -0
  28. package/lib/map.ts +438 -0
  29. package/lib/mapEngine.ts +158 -0
  30. package/lib/mapLeaflet.ts +142 -0
  31. package/lib/mapMaplibre.ts +106 -0
  32. package/lib/mapMaptiler.ts +93 -0
  33. package/lib/numericKeyboard.test.ts +179 -0
  34. package/lib/numericKeyboard.ts +166 -0
  35. package/lib/pagePadding.test.ts +37 -0
  36. package/lib/pagePadding.ts +32 -0
  37. package/lib/qrcode.test.ts +103 -0
  38. package/lib/qrcode.ts +125 -0
  39. package/lib/spreadsheetChanges.test.ts +230 -0
  40. package/lib/spreadsheetChanges.ts +331 -0
  41. package/package.json +5 -17
  42. package/shims.d.ts +19 -0
  43. package/styles/main.css +689 -884
  44. package/components/QTiptap.vue +0 -996
  45. package/components/internal/QTiptapTableView.vue +0 -123
  46. package/components/internal/QTiptapTaskItemView.vue +0 -49
  47. package/lib/tiptap-table.ts +0 -19
  48. package/lib/tiptap-task-list.ts +0 -16
@@ -0,0 +1,365 @@
1
+ // Tests unitaires de la traduction props → options du SDK MapTiler (`<q-map>`).
2
+ import { describe, expect, it } from "bun:test"
3
+ import {
4
+ DEFAULT_CENTER,
5
+ DEFAULT_MAP_HEIGHT,
6
+ DEFAULT_ZOOM,
7
+ escapeHtml,
8
+ isKnownProvider,
9
+ leafletOptionsOf,
10
+ MAPLIBRE_DEMO_STYLE,
11
+ maplibreOptionsOf,
12
+ maplibreStyle,
13
+ mapHeight,
14
+ mapOptionsOf,
15
+ mapStyleKey,
16
+ markPosition,
17
+ marksOf,
18
+ OSM_ATTRIBUTION,
19
+ OSM_STYLE,
20
+ OSM_TILES,
21
+ parsePosition,
22
+ parseZoom,
23
+ popupHtml,
24
+ providerOf,
25
+ safeCssColor,
26
+ toLeaflet,
27
+ } from "./map"
28
+
29
+ describe("providerOf", () => {
30
+ it("reconnaît les deux fournisseurs canoniques (casse et espaces ignorés)", () => {
31
+ expect(providerOf("maptiler")).toBe("maptiler")
32
+ expect(providerOf(" MapTiler ")).toBe("maptiler")
33
+ expect(providerOf("openstreetmap")).toBe("openstreetmap")
34
+ expect(providerOf("OpenStreetMap")).toBe("openstreetmap")
35
+ })
36
+
37
+ it("résout les alias osm / openstreet / open-street-map", () => {
38
+ expect(providerOf("osm")).toBe("openstreetmap")
39
+ expect(providerOf("OSM")).toBe("openstreetmap")
40
+ expect(providerOf("openstreet")).toBe("openstreetmap")
41
+ expect(providerOf("open-street-map")).toBe("openstreetmap")
42
+ })
43
+
44
+ it("reconnaît Leaflet", () => {
45
+ expect(providerOf("leaflet")).toBe("leaflet")
46
+ expect(providerOf(" Leaflet ")).toBe("leaflet")
47
+ expect(providerOf("leaflet.js")).toBe("leaflet")
48
+ })
49
+
50
+ it("reconnaît MapLibre (et son alias map-libre)", () => {
51
+ expect(providerOf("maplibre")).toBe("maplibre")
52
+ expect(providerOf(" MapLibre ")).toBe("maplibre")
53
+ expect(providerOf("map-libre")).toBe("maplibre")
54
+ expect(providerOf("maplibre-gl")).toBe("maplibre")
55
+ })
56
+
57
+ it("retombe sur maptiler quand la valeur est absente ou inconnue", () => {
58
+ expect(providerOf()).toBe("maptiler")
59
+ expect(providerOf("")).toBe("maptiler")
60
+ expect(providerOf("mapbox")).toBe("maptiler")
61
+ expect(providerOf(42)).toBe("maptiler")
62
+ })
63
+ })
64
+
65
+ describe("isKnownProvider", () => {
66
+ it("accepte les noms canoniques, les alias et l'absence de valeur", () => {
67
+ expect(isKnownProvider(undefined)).toBe(true)
68
+ expect(isKnownProvider("maptiler")).toBe(true)
69
+ expect(isKnownProvider("openstreetmap")).toBe(true)
70
+ expect(isKnownProvider("osm")).toBe(true)
71
+ expect(isKnownProvider("openstreet")).toBe(true)
72
+ expect(isKnownProvider("leaflet")).toBe(true)
73
+ expect(isKnownProvider("leaflet.js")).toBe(true)
74
+ expect(isKnownProvider("maplibre")).toBe(true)
75
+ expect(isKnownProvider("map-libre")).toBe(true)
76
+ expect(isKnownProvider("mapbox")).toBe(false)
77
+ })
78
+
79
+ it("signale une faute de frappe", () => {
80
+ expect(isKnownProvider("mapbox")).toBe(false)
81
+ expect(isKnownProvider(" maptiler ")).toBe(true)
82
+ expect(isKnownProvider(1)).toBe(false)
83
+ })
84
+ })
85
+
86
+ describe("parsePosition", () => {
87
+ it("accepte un tableau [lng, lat]", () => {
88
+ expect(parsePosition([2.35, 48.85])).toEqual([2.35, 48.85])
89
+ })
90
+
91
+ it("accepte une chaîne (JSON ou séparateurs)", () => {
92
+ expect(parsePosition("[2.35, 48.85]")).toEqual([2.35, 48.85])
93
+ expect(parsePosition("2.35,48.85")).toEqual([2.35, 48.85])
94
+ expect(parsePosition("2.35 48.85")).toEqual([2.35, 48.85])
95
+ expect(parsePosition("2.35;48.85")).toEqual([2.35, 48.85])
96
+ })
97
+
98
+ it("accepte un objet { lng, lat } (ou lon)", () => {
99
+ expect(parsePosition({ lng: 2.35, lat: 48.85 })).toEqual([2.35, 48.85])
100
+ expect(parsePosition({ lon: "-73.98", lat: "40.75" })).toEqual([-73.98, 40.75])
101
+ })
102
+
103
+ it("rejette les valeurs illisibles ou hors bornes", () => {
104
+ expect(parsePosition()).toBeUndefined()
105
+ expect(parsePosition(null)).toBeUndefined()
106
+ expect(parsePosition("Paris")).toBeUndefined()
107
+ expect(parsePosition("2.35")).toBeUndefined()
108
+ expect(parsePosition([2.35, "a"])).toBeUndefined()
109
+ expect(parsePosition([200, 10])).toBeUndefined()
110
+ expect(parsePosition([10, 95])).toBeUndefined()
111
+ expect(parsePosition([Number.NaN, 10])).toBeUndefined()
112
+ })
113
+ })
114
+
115
+ describe("parseZoom", () => {
116
+ it("lit un nombre ou une chaîne numérique", () => {
117
+ expect(parseZoom(12)).toBe(12)
118
+ expect(parseZoom("12.5")).toBe(12.5)
119
+ })
120
+
121
+ it("borne à [0, 24] et ignore l'illisible", () => {
122
+ expect(parseZoom(-3)).toBe(0)
123
+ expect(parseZoom(99)).toBe(24)
124
+ expect(parseZoom("")).toBeUndefined()
125
+ expect(parseZoom(undefined)).toBeUndefined()
126
+ })
127
+ })
128
+
129
+ describe("mapHeight", () => {
130
+ it("convertit un nombre en px", () => {
131
+ expect(mapHeight(420)).toBe("420px")
132
+ expect(mapHeight("320")).toBe("320px")
133
+ })
134
+
135
+ it("rend une longueur CSS telle quelle", () => {
136
+ expect(mapHeight("50vh")).toBe("50vh")
137
+ expect(mapHeight(" clamp(240px, 40vh, 480px) ")).toBe("clamp(240px, 40vh, 480px)")
138
+ })
139
+
140
+ it("retombe sur la hauteur par défaut", () => {
141
+ expect(mapHeight(undefined)).toBe(`${DEFAULT_MAP_HEIGHT}px`)
142
+ expect(mapHeight(0)).toBe(`${DEFAULT_MAP_HEIGHT}px`)
143
+ expect(mapHeight(" ")).toBe(`${DEFAULT_MAP_HEIGHT}px`)
144
+ })
145
+ })
146
+
147
+ describe("mapStyleKey", () => {
148
+ it("reconnaît les noms courts (casse et espaces ignorés)", () => {
149
+ expect(mapStyleKey("streets")).toBe("streets")
150
+ expect(mapStyleKey(" Streets ")).toBe("streets")
151
+ expect(mapStyleKey("dataviz")).toBe("dataviz")
152
+ })
153
+
154
+ it("laisse passer un ID ou une URL de style", () => {
155
+ expect(mapStyleKey("outdoor-v2")).toBeUndefined()
156
+ expect(mapStyleKey("https://api.maptiler.com/maps/abc/style.json")).toBeUndefined()
157
+ expect(mapStyleKey(undefined)).toBeUndefined()
158
+ })
159
+ })
160
+
161
+ describe("marques", () => {
162
+ it("résout la position (position > lng/lat)", () => {
163
+ expect(markPosition({ type: "marker", position: [2.35, 48.85] })).toEqual([2.35, 48.85])
164
+ expect(markPosition({ type: "marker", lng: 2.35, lat: 48.85 })).toEqual([2.35, 48.85])
165
+ expect(markPosition({ type: "marker", position: "2.35,48.85", lng: 0, lat: 0 })).toEqual([2.35, 48.85])
166
+ expect(markPosition({ type: "marker" })).toBeUndefined()
167
+ })
168
+
169
+ it("ne garde que les marques positionnées, dans l'ordre et avec leur index d'origine", () => {
170
+ const marks = [
171
+ { type: "marker" as const, position: [1, 2], label: "a" },
172
+ { type: "marker" as const } as any,
173
+ { type: "popup" as const, position: [3, 4], label: "b" },
174
+ ] as any
175
+
176
+ const specs = marksOf(marks)
177
+ expect(specs.map((s) => s.index)).toEqual([0, 2])
178
+ expect(specs.map((s) => s.position)).toEqual([
179
+ [1, 2],
180
+ [3, 4],
181
+ ])
182
+ expect(marksOf(null)).toEqual([])
183
+ })
184
+
185
+ it("préfère `html`, sinon échappe `label`", () => {
186
+ expect(popupHtml({ type: "marker", html: "<b>hi</b>", label: "ignoré" })).toBe("<b>hi</b>")
187
+ expect(popupHtml({ type: "marker", label: "<b>a & b</b>" })).toBe("&lt;b&gt;a &amp; b&lt;/b&gt;")
188
+ expect(popupHtml({ type: "marker" })).toBeUndefined()
189
+ expect(escapeHtml(`"quoted" & 'single'`)).toBe("&quot;quoted&quot; &amp; &#39;single&#39;")
190
+ })
191
+ })
192
+
193
+ describe("mapOptionsOf", () => {
194
+ it("applique les défauts (centre monde, zoom, style)", () => {
195
+ const options = mapOptionsOf({ style: "STREETS_OBJ" })
196
+ expect(options.center).toEqual([...DEFAULT_CENTER])
197
+ expect(options.zoom).toBe(DEFAULT_ZOOM)
198
+ expect(options.style).toBe("STREETS_OBJ")
199
+ expect(options.apiKey).toBeUndefined()
200
+ })
201
+
202
+ it("passe la clé d'API en option et les contrôles demandés", () => {
203
+ const options = mapOptionsOf({
204
+ apiKey: "KEY",
205
+ navigation: true,
206
+ geolocate: false,
207
+ scale: "bottom-left",
208
+ fullscreen: "top-left",
209
+ })
210
+
211
+ expect(options.apiKey).toBe("KEY")
212
+ expect(options.navigationControl).toBe(true)
213
+ expect(options.geolocateControl).toBe(false)
214
+ expect(options.scaleControl).toBe("bottom-left")
215
+ expect(options.fullscreenControl).toBe("top-left")
216
+ })
217
+
218
+ it("n'impose aucun contrôle non demandé (défauts du SDK)", () => {
219
+ const options = mapOptionsOf({})
220
+ expect("navigationControl" in options).toBe(false)
221
+ expect("geolocateControl" in options).toBe(false)
222
+ expect("terrain" in options).toBe(false)
223
+ expect("projection" in options).toBe(false)
224
+ })
225
+
226
+ it("force le style raster OpenStreetMap (et aucune clé) pour provider=\"openstreetmap\"", () => {
227
+ const options = mapOptionsOf({
228
+ provider: "openstreetmap",
229
+ apiKey: "KEY",
230
+ style: "streets",
231
+ })
232
+ expect(options.style).toEqual(OSM_STYLE)
233
+ expect(options.apiKey).toBeUndefined()
234
+ })
235
+
236
+ it("accepte les alias de fournisseur (osm)", () => {
237
+ const options = mapOptionsOf({ provider: "osm", style: "streets" })
238
+ expect(options.style).toEqual(OSM_STYLE)
239
+ })
240
+
241
+ it("utilise les tuiles passées pour les fournisseurs raster", () => {
242
+ const options = mapOptionsOf({
243
+ provider: "openstreetmap",
244
+ tiles: "https://tuiles.example/{z}/{x}/{y}.png",
245
+ attribution: "© Moi",
246
+ })
247
+
248
+ expect(options.style.sources.osm.tiles).toEqual(["https://tuiles.example/{z}/{x}/{y}.png"])
249
+ expect(options.style.sources.osm.attribution).toBe("© Moi")
250
+ })
251
+
252
+ it("fusionne `options` en dernier", () => {
253
+ const options = mapOptionsOf({
254
+ center: [1, 2],
255
+ zoom: 5,
256
+ options: { center: [3, 4], zoom: 9, interactive: false, maxZoom: 14 },
257
+ })
258
+ expect(options.center).toEqual([3, 4])
259
+ expect(options.zoom).toBe(9)
260
+ expect(options.interactive).toBe(false)
261
+ expect(options.maxZoom).toBe(14)
262
+ })
263
+ })
264
+
265
+ describe("toLeaflet / safeCssColor", () => {
266
+ it("inverse lng/lat en lat/lng", () => {
267
+ expect(toLeaflet([2.35, 48.85])).toEqual([48.85, 2.35])
268
+ })
269
+
270
+ it("laisse passer les couleurs CSS et rejette le reste", () => {
271
+ expect(safeCssColor("#1976d2")).toBe("#1976d2")
272
+ expect(safeCssColor(" rgb(25, 118, 210) ")).toBe("rgb(25, 118, 210)")
273
+ expect(safeCssColor("oklch(0.5 0.1 200)")).toBe("oklch(0.5 0.1 200)")
274
+ expect(safeCssColor('red" onload="alert(1)')).toBeUndefined()
275
+ expect(safeCssColor("<script>")).toBeUndefined()
276
+ expect(safeCssColor("red;background:url(x)")).toBeUndefined()
277
+ expect(safeCssColor(undefined)).toBeUndefined()
278
+ })
279
+ })
280
+
281
+ describe("leafletOptionsOf", () => {
282
+ it("convertit le centre en [lat, lng] et applique les défauts de tuiles", () => {
283
+ const options = leafletOptionsOf({ center: "[2.35, 48.85]", zoom: "12" })
284
+
285
+ expect(options.center).toEqual([48.85, 2.35])
286
+ expect(options.zoom).toBe(12)
287
+ expect(options.tiles).toBe(OSM_TILES)
288
+ expect(options.attribution).toBe(OSM_ATTRIBUTION)
289
+ expect(options.mapOptions).toEqual({})
290
+ expect(options.layerOptions).toEqual({})
291
+ })
292
+
293
+ it("répartit les options utilisateur entre L.map() et L.tileLayer()", () => {
294
+ const options = leafletOptionsOf({
295
+ tiles: " https://tuiles.example/{z}/{x}/{y}.png ",
296
+ attribution: "© Moi",
297
+ options: { zoomControl: false, maxZoom: 18, tileLayer: { detectRetina: true } },
298
+ })
299
+
300
+ expect(options.tiles).toBe("https://tuiles.example/{z}/{x}/{y}.png")
301
+ expect(options.attribution).toBe("© Moi")
302
+ expect(options.mapOptions).toEqual({ zoomControl: false, maxZoom: 18 })
303
+ expect(options.layerOptions).toEqual({ detectRetina: true })
304
+ })
305
+
306
+ it("retombe sur la vue monde sans centre ni zoom", () => {
307
+ const options = leafletOptionsOf()
308
+ expect(options.center).toEqual(toLeaflet([...DEFAULT_CENTER]))
309
+ expect(options.zoom).toBe(DEFAULT_ZOOM)
310
+ })
311
+ })
312
+
313
+ describe("maplibreStyle / maplibreOptionsOf", () => {
314
+ it("utilise le style de démonstration par défaut (et pour `streets`)", () => {
315
+ expect(maplibreStyle().style).toBe(MAPLIBRE_DEMO_STYLE)
316
+ expect(maplibreStyle().known).toBe(true)
317
+ expect(maplibreStyle({ style: "streets" }).style).toBe(MAPLIBRE_DEMO_STYLE)
318
+ expect(maplibreStyle({ style: "demotiles" }).style).toBe(MAPLIBRE_DEMO_STYLE)
319
+ })
320
+
321
+ it("reconnaît le raster OpenStreetMap, tuiles personnalisées comprises", () => {
322
+ const osm = maplibreStyle({ style: "openstreetmap" })
323
+ expect(osm.known).toBe(true)
324
+ expect(osm.style.sources.osm.tiles).toEqual([OSM_TILES])
325
+
326
+ const custom = maplibreStyle({
327
+ style: "osm",
328
+ tiles: "https://tuiles.example/{z}/{x}/{y}.png",
329
+ attribution: "© Moi",
330
+ })
331
+ expect(custom.style.sources.osm.tiles).toEqual(["https://tuiles.example/{z}/{x}/{y}.png"])
332
+ expect(custom.style.sources.osm.attribution).toBe("© Moi")
333
+ })
334
+
335
+ it("laisse passer une URL ou un objet de style", () => {
336
+ expect(maplibreStyle({ style: "https://styles.example/style.json" }).style).toBe(
337
+ "https://styles.example/style.json",
338
+ )
339
+ expect(maplibreStyle({ style: "/styles/local.json" }).style).toBe("/styles/local.json")
340
+
341
+ const object = { version: 8, sources: {}, layers: [] }
342
+ expect(maplibreStyle({ style: object as any }).style).toBe(object)
343
+ })
344
+
345
+ it("signale un nom court inconnu et retombe sur le style de démonstration", () => {
346
+ const unknown = maplibreStyle({ style: "topo" })
347
+ expect(unknown.known).toBe(false)
348
+ expect(unknown.style).toBe(MAPLIBRE_DEMO_STYLE)
349
+ })
350
+
351
+ it("construit les options de MapLibre (centre, zoom, style, options brutes)", () => {
352
+ const options = maplibreOptionsOf({
353
+ center: "[2.35, 48.85]",
354
+ zoom: "12",
355
+ options: { maxZoom: 18, pitch: 45 },
356
+ })
357
+
358
+ expect(options.map.center).toEqual([2.35, 48.85])
359
+ expect(options.map.zoom).toBe(12)
360
+ expect(options.map.style).toBe(MAPLIBRE_DEMO_STYLE)
361
+ expect(options.map.maxZoom).toBe(18)
362
+ expect(options.map.pitch).toBe(45)
363
+ expect(options.styleKnown).toBe(true)
364
+ })
365
+ })