@dnax/ui 0.0.41 → 0.0.43

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.
@@ -393,10 +393,13 @@ const onDocMousedown = (e: MouseEvent) => {
393
393
  }
394
394
 
395
395
  onMounted(() => {
396
- if (typeof document !== "undefined") document.addEventListener("mousedown", onDocMousedown)
396
+ // Phase de CAPTURE (3e arg = true) : un `@mousedown.stop` plus haut dans l'arbre
397
+ // (contenu de QDialog / QDataGrid…) empêcherait sinon l'événement d'atteindre
398
+ // `document` et le popup ne se fermerait jamais au clic extérieur.
399
+ if (typeof document !== "undefined") document.addEventListener("mousedown", onDocMousedown, true)
397
400
  })
398
401
  onBeforeUnmount(() => {
399
- if (typeof document !== "undefined") document.removeEventListener("mousedown", onDocMousedown)
402
+ if (typeof document !== "undefined") document.removeEventListener("mousedown", onDocMousedown, true)
400
403
  window.clearTimeout(swipeCloseTimer)
401
404
  })
402
405
 
@@ -339,10 +339,12 @@ const menuRows = computed<MenuRow[]>(() => {
339
339
  })
340
340
 
341
341
  onMounted(() => {
342
- if (typeof document !== "undefined") document.addEventListener("mousedown", onDocMousedown)
342
+ // Phase de CAPTURE : sinon un `@mousedown.stop` parent (contenu de QDialog,
343
+ // QDataGrid…) bloque l'événement avant `document` → popup jamais fermé.
344
+ if (typeof document !== "undefined") document.addEventListener("mousedown", onDocMousedown, true)
343
345
  })
344
346
  onBeforeUnmount(() => {
345
- if (typeof document !== "undefined") document.removeEventListener("mousedown", onDocMousedown)
347
+ if (typeof document !== "undefined") document.removeEventListener("mousedown", onDocMousedown, true)
346
348
  disableTracking()
347
349
  })
348
350
  </script>
@@ -210,13 +210,15 @@ watch(open, (v) => {
210
210
 
211
211
  onMounted(() => {
212
212
  if (typeof document !== "undefined") {
213
- document.addEventListener("mousedown", onDocMousedown)
213
+ // Phase de CAPTURE : un `@mousedown.stop` parent (contenu de QDialog…) bloque
214
+ // sinon l'événement avant `document` → panneau jamais fermé au clic extérieur.
215
+ document.addEventListener("mousedown", onDocMousedown, true)
214
216
  document.addEventListener("keydown", onDocKeydown)
215
217
  }
216
218
  })
217
219
  onBeforeUnmount(() => {
218
220
  if (typeof document !== "undefined") {
219
- document.removeEventListener("mousedown", onDocMousedown)
221
+ document.removeEventListener("mousedown", onDocMousedown, true)
220
222
  document.removeEventListener("keydown", onDocKeydown)
221
223
  document.body.style.overflow = ""
222
224
  }
@@ -149,13 +149,15 @@ watch(open, (v) => {
149
149
 
150
150
  onMounted(() => {
151
151
  if (typeof document !== "undefined") {
152
- document.addEventListener("mousedown", onDocMousedown)
152
+ // Phase de CAPTURE : un `@mousedown.stop` parent (contenu de QDialog…) bloque
153
+ // sinon l'événement avant `document` → calendrier jamais fermé au clic extérieur.
154
+ document.addEventListener("mousedown", onDocMousedown, true)
153
155
  document.addEventListener("keydown", onDocKeydown)
154
156
  }
155
157
  })
156
158
  onBeforeUnmount(() => {
157
159
  if (typeof document !== "undefined") {
158
- document.removeEventListener("mousedown", onDocMousedown)
160
+ document.removeEventListener("mousedown", onDocMousedown, true)
159
161
  document.removeEventListener("keydown", onDocKeydown)
160
162
  document.body.style.overflow = ""
161
163
  }
@@ -56,12 +56,14 @@ const onDocPointerDown = (e: PointerEvent) => {
56
56
  }
57
57
  onMounted(() => {
58
58
  if (typeof document !== "undefined") {
59
- document.addEventListener("pointerdown", onDocPointerDown)
59
+ // Phase de CAPTURE : un `@pointerdown.stop` parent bloque sinon l'événement
60
+ // avant `document` → actions du FAB jamais repliées au clic extérieur.
61
+ document.addEventListener("pointerdown", onDocPointerDown, true)
60
62
  }
61
63
  })
62
64
  onBeforeUnmount(() => {
63
65
  if (typeof document !== "undefined") {
64
- document.removeEventListener("pointerdown", onDocPointerDown)
66
+ document.removeEventListener("pointerdown", onDocPointerDown, true)
65
67
  }
66
68
  })
67
69
 
@@ -61,10 +61,12 @@ const onKeydown = (e: KeyboardEvent) => {
61
61
  }
62
62
 
63
63
  onMounted(() => {
64
- if (typeof document !== "undefined") document.addEventListener("mousedown", onDocMousedown)
64
+ // Phase de CAPTURE : un `@mousedown.stop` parent (contenu de QDialog…) bloque
65
+ // sinon l'événement avant `document` → menu jamais fermé au clic extérieur.
66
+ if (typeof document !== "undefined") document.addEventListener("mousedown", onDocMousedown, true)
65
67
  })
66
68
  onBeforeUnmount(() => {
67
- if (typeof document !== "undefined") document.removeEventListener("mousedown", onDocMousedown)
69
+ if (typeof document !== "undefined") document.removeEventListener("mousedown", onDocMousedown, true)
68
70
  })
69
71
  </script>
70
72
 
@@ -603,13 +603,15 @@ watch(open, (v) => {
603
603
 
604
604
  onMounted(() => {
605
605
  if (typeof document !== "undefined") {
606
- document.addEventListener("mousedown", onDocMousedown)
606
+ // Phase de CAPTURE : un `@mousedown.stop` parent (contenu de QDialog…) bloque
607
+ // sinon l'événement avant `document` → popup jamais fermé au clic extérieur.
608
+ document.addEventListener("mousedown", onDocMousedown, true)
607
609
  document.addEventListener("keydown", onDocKeydown)
608
610
  }
609
611
  })
610
612
  onBeforeUnmount(() => {
611
613
  if (typeof document !== "undefined") {
612
- document.removeEventListener("mousedown", onDocMousedown)
614
+ document.removeEventListener("mousedown", onDocMousedown, true)
613
615
  document.removeEventListener("keydown", onDocKeydown)
614
616
  document.body.style.overflow = ""
615
617
  }
@@ -20,8 +20,12 @@ export type QSpreadsheetCellType =
20
20
  export interface QSpreadsheetCellOption {
21
21
  /** Valeur stockée dans la cellule */
22
22
  value: any
23
- /** Libellé affiché */
24
- label: string
23
+ /**
24
+ * Libellé affiché. Les nombres sont acceptés (`{ value: 1, label: 1 }` pour une
25
+ * note, un niveau…) : le composant les normalise en string partout où il fait des
26
+ * opérations de chaîne (recherche, filtre, tri, éditeur).
27
+ */
28
+ label: string | number
25
29
  /** Couleur du badge (token ou hex, ex. "positive", "#22c55e") */
26
30
  color?: string
27
31
  }
@@ -400,12 +404,18 @@ const newRowKey = (): string => {
400
404
  return `row-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`
401
405
  }
402
406
 
403
- /** Injecte `_key` (mutatif) sur les lignes qui n'en ont pas encore */
407
+ /**
408
+ * Injecte `_key` (mutatif) uniquement sur les lignes qui n'en ont pas.
409
+ * Une clé DÉJÀ PRÉSENTE est toujours conservée, quelle que soit sa valeur
410
+ * (string, nombre, uuid ou non) : l'injection ne s'applique qu'à
411
+ * `undefined` / `null` / chaîne vide. Aucune réécriture, aucun dédoublonnage.
412
+ */
404
413
  const ensureRowKeys = (rows: Record<string, any>[] | undefined) => {
405
414
  if (!Array.isArray(rows)) return
406
415
  for (const row of rows) {
407
416
  if (!row || typeof row !== "object") continue
408
- if (typeof row[ROW_KEY] !== "string" || !row[ROW_KEY]) row[ROW_KEY] = newRowKey()
417
+ const current = row[ROW_KEY]
418
+ if (current === undefined || current === null || current === "") row[ROW_KEY] = newRowKey()
409
419
  }
410
420
  }
411
421
 
@@ -687,22 +697,19 @@ const editingIsFormula = computed(() => {
687
697
  if (!editing.value) return false
688
698
  const col = colOf(editing.value.column)
689
699
  if (!col || col.type === "boolean" || col.type === "select") return false
690
- return draft.value.trim().startsWith("=")
700
+ return String(draft.value ?? "").trim().startsWith("=")
691
701
  })
692
702
  const editingIsSelect = computed(() => {
693
703
  const col = editing.value ? colOf(editing.value.column) : undefined
694
704
  return col?.type === "select"
695
705
  })
696
706
  const draftLines = computed(() =>
697
- Math.min(5, Math.max(1, draft.value.split("\n").length)),
707
+ Math.min(5, Math.max(1, String(draft.value ?? "").split("\n").length)),
698
708
  )
699
709
 
700
710
  const editorInputType = computed(() => {
701
711
  const col = editing.value ? colOf(editing.value.column) : undefined
702
712
  switch (col?.type) {
703
- case "number":
704
- case "integer":
705
- return "number"
706
713
  case "email":
707
714
  return "email"
708
715
  case "url":
@@ -712,15 +719,20 @@ const editorInputType = computed(() => {
712
719
  case "datetime":
713
720
  return "datetime-local"
714
721
  default:
722
+ // PAS de `type="number"` pour number/integer : (1) le navigateur refuse
723
+ // `=` (formule) ou un "-" isolé dans un input number, (2) `v-model` sur
724
+ // input[type=number] **caste la valeur en Number** → le brouillon n'était
725
+ // plus une string et tous les `.trim()` / `.split()` dessus explosaient.
726
+ // Le clavier numérique mobile est assuré par `inputmode` (`editorInputMode`).
715
727
  return "text"
716
728
  }
717
729
  })
718
730
 
719
- /** step natif : "any" (décimales) pour number, "1" pour integer */
720
- const editorStep = computed(() => {
731
+ /** Équivalent mobile de `type="number"` : clavier décimal / numérique sans cast */
732
+ const editorInputMode = computed(() => {
721
733
  const col = editing.value ? colOf(editing.value.column) : undefined
722
- if (col?.type === "number") return "any"
723
- if (col?.type === "integer") return "1"
734
+ if (col?.type === "number") return "decimal"
735
+ if (col?.type === "integer") return "numeric"
724
736
  return undefined
725
737
  })
726
738
 
@@ -757,7 +769,7 @@ const positionEditor = () => {
757
769
  // Une formule élargit l'éditeur (monospace ≈ 7.6px/car), sans dépasser le bord droit
758
770
  const maxW = sc.width - (tr.left - sc.left) - 4
759
771
  const ideal = editingIsFormula.value
760
- ? Math.min(480, Math.max(220, draft.value.length * 7.6 + 30))
772
+ ? Math.min(480, Math.max(220, String(draft.value ?? "").length * 7.6 + 30))
761
773
  : tr.width
762
774
  const width = Math.min(maxW, Math.max(tr.width, ideal))
763
775
  editorStyle.value = {
@@ -1499,7 +1511,7 @@ const selectOptions = computed<QSpreadsheetCellOption[]>(() => {
1499
1511
  if (!editing.value) return []
1500
1512
  const col = colOf(editing.value.column)
1501
1513
  if (col?.type !== "select") return []
1502
- const q = draft.value.trim().toLowerCase()
1514
+ const q = String(draft.value ?? "").trim().toLowerCase()
1503
1515
  return (col.options ?? []).filter(
1504
1516
  (o) => !q || String(o.label ?? o.value ?? "").toLowerCase().includes(q),
1505
1517
  )
@@ -1689,8 +1701,8 @@ const acceptFx = () => {
1689
1701
  const f = fxSuggestions.value[fxActive.value]
1690
1702
  if (!f) return
1691
1703
  const replaceTail = (s: string) => s.replace(/[A-Za-z]+$/, f.name + "(")
1692
- if (editing.value) draft.value = replaceTail(draft.value)
1693
- else fxDraft.value = replaceTail(fxDraft.value)
1704
+ if (editing.value) draft.value = replaceTail(String(draft.value ?? ""))
1705
+ else fxDraft.value = replaceTail(String(fxDraft.value ?? ""))
1694
1706
  fxActive.value = 0
1695
1707
  nextTick(() => {
1696
1708
  const el = editing.value
@@ -2631,7 +2643,9 @@ const onFilterSearch = (e: Event) => {
2631
2643
 
2632
2644
  onMounted(() => {
2633
2645
  window.addEventListener("pointerup", onPointerUpGlobal)
2634
- window.addEventListener("pointerdown", onDocPointerDown)
2646
+ // Phase de CAPTURE : un `@pointerdown.stop` parent bloque sinon l'événement
2647
+ // avant `window` → menu contextuel / filtre jamais fermé au clic extérieur.
2648
+ window.addEventListener("pointerdown", onDocPointerDown, true)
2635
2649
  window.addEventListener("resize", onResize)
2636
2650
  nextTick(() => {
2637
2651
  updateViewportMetrics()
@@ -2640,7 +2654,7 @@ onMounted(() => {
2640
2654
  })
2641
2655
  onBeforeUnmount(() => {
2642
2656
  window.removeEventListener("pointerup", onPointerUpGlobal)
2643
- window.removeEventListener("pointerdown", onDocPointerDown)
2657
+ window.removeEventListener("pointerdown", onDocPointerDown, true)
2644
2658
  window.removeEventListener("resize", onResize)
2645
2659
  endResize()
2646
2660
  endRowResize()
@@ -4377,7 +4391,7 @@ defineExpose({
4377
4391
  v-model="draft"
4378
4392
  class="q-spreadsheet__editor-input"
4379
4393
  :type="editorInputType"
4380
- :step="editorStep"
4394
+ :inputmode="editorInputMode"
4381
4395
  :placeholder="editingCol.type === 'select' ? 'Type to filter…' : ''"
4382
4396
  @keydown="onEditKeydown"
4383
4397
  @blur="commitEdit"
@@ -150,12 +150,12 @@ const onDocPointerDown = (e: PointerEvent) => {
150
150
  }
151
151
 
152
152
  watch(opened, (v) => {
153
- if (v) document.addEventListener("pointerdown", onDocPointerDown)
154
- else document.removeEventListener("pointerdown", onDocPointerDown)
153
+ if (v) document.addEventListener("pointerdown", onDocPointerDown, true)
154
+ else document.removeEventListener("pointerdown", onDocPointerDown, true)
155
155
  })
156
156
 
157
157
  onBeforeUnmount(() => {
158
- document.removeEventListener("pointerdown", onDocPointerDown)
158
+ document.removeEventListener("pointerdown", onDocPointerDown, true)
159
159
  if (wrapperEl.value) wrapperEl.value.style.removeProperty("transition")
160
160
  })
161
161
  </script>
@@ -293,12 +293,14 @@ watch(colorOpen, (open) => {
293
293
  if (e.key === "Escape") colorOpen.value = false
294
294
  }
295
295
  const close = () => { colorOpen.value = false }
296
- document.addEventListener("mousedown", onDown)
296
+ // Phase de CAPTURE : un `@mousedown.stop` parent bloque sinon l'événement avant
297
+ // `document` → palette de couleurs jamais fermée au clic extérieur.
298
+ document.addEventListener("mousedown", onDown, true)
297
299
  document.addEventListener("keydown", onKey)
298
300
  window.addEventListener("blur", close)
299
301
  window.addEventListener("resize", close)
300
302
  return () => {
301
- document.removeEventListener("mousedown", onDown)
303
+ document.removeEventListener("mousedown", onDown, true)
302
304
  document.removeEventListener("keydown", onKey)
303
305
  window.removeEventListener("blur", close)
304
306
  window.removeEventListener("resize", close)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dnax/ui",
3
- "version": "0.0.41",
3
+ "version": "0.0.43",
4
4
  "module": "index.ts",
5
5
  "description": "UI design System",
6
6
  "type": "module",