@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.
- package/components/QAutocomplete.vue +5 -2
- package/components/QBtnActions.vue +4 -2
- package/components/QCountryPicker.vue +4 -2
- package/components/QDatePicker.vue +4 -2
- package/components/QFab.vue +4 -2
- package/components/QNavMenu.vue +4 -2
- package/components/QSelect.vue +4 -2
- package/components/QSpreadsheet.vue +34 -20
- package/components/QSwipeCell.vue +3 -3
- package/components/QTiptap.vue +4 -2
- package/package.json +1 -1
|
@@ -393,10 +393,13 @@ const onDocMousedown = (e: MouseEvent) => {
|
|
|
393
393
|
}
|
|
394
394
|
|
|
395
395
|
onMounted(() => {
|
|
396
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
}
|
package/components/QFab.vue
CHANGED
|
@@ -56,12 +56,14 @@ const onDocPointerDown = (e: PointerEvent) => {
|
|
|
56
56
|
}
|
|
57
57
|
onMounted(() => {
|
|
58
58
|
if (typeof document !== "undefined") {
|
|
59
|
-
|
|
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
|
|
package/components/QNavMenu.vue
CHANGED
|
@@ -61,10 +61,12 @@ const onKeydown = (e: KeyboardEvent) => {
|
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
onMounted(() => {
|
|
64
|
-
|
|
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
|
|
package/components/QSelect.vue
CHANGED
|
@@ -603,13 +603,15 @@ watch(open, (v) => {
|
|
|
603
603
|
|
|
604
604
|
onMounted(() => {
|
|
605
605
|
if (typeof document !== "undefined") {
|
|
606
|
-
|
|
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
|
-
/**
|
|
24
|
-
|
|
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
|
-
/**
|
|
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
|
-
|
|
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
|
-
/**
|
|
720
|
-
const
|
|
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 "
|
|
723
|
-
if (col?.type === "integer") return "
|
|
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
|
-
|
|
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
|
-
:
|
|
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>
|
package/components/QTiptap.vue
CHANGED
|
@@ -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
|
-
|
|
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)
|