@dnax/ui 0.0.10 → 0.0.12
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/QBottomSheetFooter.vue +7 -2
- package/components/QBottomSheetHeader.vue +23 -16
- package/components/QContainer.vue +47 -1
- package/components/QDialog.vue +2 -2
- package/components/QDialogContent.vue +20 -0
- package/components/QDialogFooter.vue +7 -2
- package/components/QDialogHeader.vue +24 -17
- package/components/QDialogProvider.vue +20 -26
- package/components/QFooter.vue +7 -0
- package/components/QPage.vue +3 -2
- package/components/QTab.vue +4 -2
- package/components/QTabs.vue +39 -0
- package/components/internal/QDialogHost.vue +45 -0
- package/index.ts +2 -1
- package/lib/fixedLayout.ts +62 -16
- package/lib/platform.ts +185 -19
- package/lib/q.ts +35 -2
- package/package.json +1 -1
- package/scripts/generate-exports.ts +1 -1
- package/styles/main.css +273 -32
package/lib/fixedLayout.ts
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
|
-
// fixedLayout — offset automatique des barres fixed (q-header, q-back-header
|
|
2
|
-
// et du q-page qui les
|
|
1
|
+
// fixedLayout — offset automatique des barres fixed (q-header, q-back-header,
|
|
2
|
+
// q-footer) et du q-page qui les entoure.
|
|
3
3
|
//
|
|
4
4
|
// Problème : les barres fixed sortent du flux (position: fixed) → le contenu de
|
|
5
5
|
// q-page passe dessous. Ce module :
|
|
6
6
|
// - q-page (mode "page") reçoit un padding-top = hauteur cumulée des barres
|
|
7
|
-
// fixed qui le précèdent
|
|
7
|
+
// fixed HAUT qui le précèdent ET un padding-bottom = hauteur cumulée des
|
|
8
|
+
// barres fixed BAS qui le suivent (le contenu n'est masqué ni en haut ni en
|
|
9
|
+
// bas au scroll)
|
|
8
10
|
// - chaque barre fixed suivante (mode "bar") s'empile sous la précédente (top)
|
|
11
|
+
// - chaque barre fixed basse (mode "bar-bottom") s'empile au-dessus de la
|
|
12
|
+
// suivante (bottom)
|
|
9
13
|
//
|
|
10
14
|
// Les barres fixed sont cherchées dans TOUT le sous-arbre de l'enveloppe q-app
|
|
11
15
|
// (pas seulement parmi les frères directs) et ordonnées par position dans le
|
|
@@ -15,7 +19,12 @@
|
|
|
15
19
|
import { onBeforeUnmount, onMounted, watch } from "vue"
|
|
16
20
|
import type { Ref } from "vue"
|
|
17
21
|
|
|
18
|
-
|
|
22
|
+
/** Barres fixed en haut de l'écran */
|
|
23
|
+
export const FIXED_TOP_SELECTOR = ".q-header--fixed, .q-back-header--fixed"
|
|
24
|
+
/** Barres fixed en bas de l'écran */
|
|
25
|
+
export const FIXED_BOTTOM_SELECTOR = ".q-footer--fixed"
|
|
26
|
+
/** Toutes les barres fixed (haut + bas) */
|
|
27
|
+
export const FIXED_BAR_SELECTOR = `${FIXED_TOP_SELECTOR}, ${FIXED_BOTTOM_SELECTOR}`
|
|
19
28
|
|
|
20
29
|
/** Racine du layout : le .q-app le plus proche, sinon le parent direct. */
|
|
21
30
|
function layoutRoot(el: HTMLElement): HTMLElement | null {
|
|
@@ -23,13 +32,13 @@ function layoutRoot(el: HTMLElement): HTMLElement | null {
|
|
|
23
32
|
}
|
|
24
33
|
|
|
25
34
|
/**
|
|
26
|
-
* Hauteur cumulée des barres fixed qui précèdent `el` dans l'ordre du
|
|
27
|
-
* sous la même enveloppe q-app. `el` lui-même (cas mode "bar") est exclu.
|
|
35
|
+
* Hauteur cumulée des barres fixed HAUT qui précèdent `el` dans l'ordre du
|
|
36
|
+
* document, sous la même enveloppe q-app. `el` lui-même (cas mode "bar") est exclu.
|
|
28
37
|
*/
|
|
29
38
|
export function fixedBarsHeightBefore(el: HTMLElement): number {
|
|
30
39
|
const root = layoutRoot(el)
|
|
31
40
|
if (!root) return 0
|
|
32
|
-
const bars = Array.from(root.querySelectorAll<HTMLElement>(
|
|
41
|
+
const bars = Array.from(root.querySelectorAll<HTMLElement>(FIXED_TOP_SELECTOR))
|
|
33
42
|
let total = 0
|
|
34
43
|
for (const bar of bars) {
|
|
35
44
|
if (bar === el) continue
|
|
@@ -41,6 +50,26 @@ export function fixedBarsHeightBefore(el: HTMLElement): number {
|
|
|
41
50
|
return total
|
|
42
51
|
}
|
|
43
52
|
|
|
53
|
+
/**
|
|
54
|
+
* Hauteur cumulée des barres fixed BAS qui SUIVENT `el` dans l'ordre du
|
|
55
|
+
* document, sous la même enveloppe q-app. `el` lui-même (cas mode "bar-bottom")
|
|
56
|
+
* est exclu.
|
|
57
|
+
*/
|
|
58
|
+
export function fixedBarsHeightAfter(el: HTMLElement): number {
|
|
59
|
+
const root = layoutRoot(el)
|
|
60
|
+
if (!root) return 0
|
|
61
|
+
const bars = Array.from(root.querySelectorAll<HTMLElement>(FIXED_BOTTOM_SELECTOR))
|
|
62
|
+
let total = 0
|
|
63
|
+
for (const bar of bars) {
|
|
64
|
+
if (bar === el) continue
|
|
65
|
+
// DOCUMENT_POSITION_PRECEDING = la barre est APRÈS el dans le document
|
|
66
|
+
if (bar.compareDocumentPosition(el) & Node.DOCUMENT_POSITION_PRECEDING) {
|
|
67
|
+
total += bar.offsetHeight
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return total
|
|
71
|
+
}
|
|
72
|
+
|
|
44
73
|
/** Toutes les barres fixed sous l'enveloppe q-app (pour l'observation). */
|
|
45
74
|
function fixedBarsInRoot(el: HTMLElement): HTMLElement[] {
|
|
46
75
|
const root = layoutRoot(el)
|
|
@@ -49,15 +78,19 @@ function fixedBarsInRoot(el: HTMLElement): HTMLElement[] {
|
|
|
49
78
|
}
|
|
50
79
|
|
|
51
80
|
/**
|
|
52
|
-
* Applique l'offset lié aux barres fixed qui
|
|
53
|
-
* - mode "page"
|
|
54
|
-
*
|
|
81
|
+
* Applique l'offset lié aux barres fixed qui entourent `el` :
|
|
82
|
+
* - mode "page" : padding-top = barres HAUT précédentes,
|
|
83
|
+
* padding-bottom = barres BAS suivantes
|
|
84
|
+
* (+ variables --q-page-offset / --q-page-offset-bottom)
|
|
85
|
+
* - mode "bar" : top = hauteur cumulée (empilement des barres fixed HAUT)
|
|
86
|
+
* - mode "bar-bottom" : bottom = hauteur cumulée (empilement des footers fixed
|
|
87
|
+
* depuis le bas de l'écran)
|
|
55
88
|
*
|
|
56
89
|
* `enabled` permet de n'activer que dans certaines conditions (ex. props.fixed).
|
|
57
90
|
*/
|
|
58
91
|
export function useFixedBarOffset(
|
|
59
92
|
el: Ref<HTMLElement | null>,
|
|
60
|
-
mode: "page" | "bar",
|
|
93
|
+
mode: "page" | "bar" | "bar-bottom",
|
|
61
94
|
enabled: () => boolean = () => true,
|
|
62
95
|
) {
|
|
63
96
|
let ro: ResizeObserver | null = null
|
|
@@ -75,14 +108,22 @@ export function useFixedBarOffset(
|
|
|
75
108
|
const apply = () => {
|
|
76
109
|
const node = el.value
|
|
77
110
|
if (!node || !enabled()) return
|
|
78
|
-
const h = fixedBarsHeightBefore(node)
|
|
79
111
|
if (mode === "page") {
|
|
80
|
-
|
|
81
|
-
|
|
112
|
+
const top = fixedBarsHeightBefore(node)
|
|
113
|
+
const bottom = fixedBarsHeightAfter(node)
|
|
114
|
+
node.style.setProperty("--q-page-offset", top ? `${top}px` : "0px")
|
|
115
|
+
node.style.setProperty("--q-page-offset-bottom", bottom ? `${bottom}px` : "0px")
|
|
116
|
+
node.style.paddingTop = top ? `${top}px` : ""
|
|
117
|
+
node.style.paddingBottom = bottom ? `${bottom}px` : ""
|
|
82
118
|
}
|
|
83
|
-
else {
|
|
119
|
+
else if (mode === "bar") {
|
|
120
|
+
const h = fixedBarsHeightBefore(node)
|
|
84
121
|
node.style.top = h ? `${h}px` : ""
|
|
85
122
|
}
|
|
123
|
+
else {
|
|
124
|
+
const h = fixedBarsHeightAfter(node)
|
|
125
|
+
node.style.bottom = h ? `${h}px` : ""
|
|
126
|
+
}
|
|
86
127
|
}
|
|
87
128
|
|
|
88
129
|
const clear = () => {
|
|
@@ -90,11 +131,16 @@ export function useFixedBarOffset(
|
|
|
90
131
|
if (!node) return
|
|
91
132
|
if (mode === "page") {
|
|
92
133
|
node.style.removeProperty("--q-page-offset")
|
|
134
|
+
node.style.removeProperty("--q-page-offset-bottom")
|
|
93
135
|
node.style.paddingTop = ""
|
|
136
|
+
node.style.paddingBottom = ""
|
|
94
137
|
}
|
|
95
|
-
else {
|
|
138
|
+
else if (mode === "bar") {
|
|
96
139
|
node.style.top = ""
|
|
97
140
|
}
|
|
141
|
+
else {
|
|
142
|
+
node.style.bottom = ""
|
|
143
|
+
}
|
|
98
144
|
}
|
|
99
145
|
|
|
100
146
|
onMounted(() => {
|
package/lib/platform.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// $q.platform — détection de plateforme (API groupée sous `is`)
|
|
2
2
|
// + breakpoints réactifs (VueUse useBreakpoints, tailles Quasar).
|
|
3
|
+
// API alignée sur Quasar : https://quasar.dev/options/platform-detection
|
|
3
4
|
import { useBreakpoints } from "@vueuse/core"
|
|
4
5
|
|
|
5
6
|
export interface QPlatformIs {
|
|
@@ -9,6 +10,8 @@ export interface QPlatformIs {
|
|
|
9
10
|
desktop: boolean
|
|
10
11
|
/** mobile ET Capacitor/Cordova (app native) */
|
|
11
12
|
nativeMobile: boolean
|
|
13
|
+
/** nom du wrapper natif : 'cordova' | 'capacitor' | undefined */
|
|
14
|
+
nativeMobileWrapper?: "cordova" | "capacitor"
|
|
12
15
|
ios: boolean
|
|
13
16
|
android: boolean
|
|
14
17
|
iphone: boolean
|
|
@@ -22,12 +25,40 @@ export interface QPlatformIs {
|
|
|
22
25
|
firefox: boolean
|
|
23
26
|
opera: boolean
|
|
24
27
|
safari: boolean
|
|
28
|
+
vivaldi: boolean
|
|
25
29
|
ie: boolean
|
|
30
|
+
/** moteur WebKit (Safari, Chrome, Edge…) */
|
|
31
|
+
webkit: boolean
|
|
32
|
+
/** extension navigateur (BEX — Chrome/Firefox) */
|
|
33
|
+
bex: boolean
|
|
34
|
+
/** Chrome OS (Chromebook) */
|
|
35
|
+
cros: boolean
|
|
36
|
+
blackberry: boolean
|
|
37
|
+
winphone: boolean
|
|
38
|
+
/** Amazon Silk (Kindle) */
|
|
39
|
+
silk: boolean
|
|
26
40
|
cordova: boolean
|
|
27
41
|
capacitor: boolean
|
|
28
42
|
electron: boolean
|
|
43
|
+
/** écran tactile */
|
|
29
44
|
touch: boolean
|
|
45
|
+
/** pointeur précis (souris) disponible */
|
|
30
46
|
mouse: boolean
|
|
47
|
+
/** nom du navigateur : 'chrome' | 'firefox' | 'safari' | … | 'generic' */
|
|
48
|
+
name: string
|
|
49
|
+
/** version complète du navigateur ('70.0.3538.110') */
|
|
50
|
+
version: string
|
|
51
|
+
/** version majeure du navigateur (70, ou -1 si inconnue) */
|
|
52
|
+
versionNumber: number
|
|
53
|
+
/** nom de l'OS : 'mac' | 'win' | 'linux' | 'ios' | 'android' | 'cros' | … */
|
|
54
|
+
platform: string
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface QPlatformHas {
|
|
58
|
+
/** écran tactile capable */
|
|
59
|
+
touch: boolean
|
|
60
|
+
/** localStorage / sessionStorage disponibles */
|
|
61
|
+
webStorage: boolean
|
|
31
62
|
}
|
|
32
63
|
|
|
33
64
|
export interface QPlatform {
|
|
@@ -38,25 +69,111 @@ export interface QPlatform {
|
|
|
38
69
|
isClient: boolean
|
|
39
70
|
/** booléens de détection groupés : $q.platform.is.mobile, is.ios, … */
|
|
40
71
|
is: QPlatformIs
|
|
72
|
+
/** capacités : has.touch, has.webStorage */
|
|
73
|
+
has: QPlatformHas
|
|
41
74
|
within: { iframe: boolean }
|
|
42
75
|
}
|
|
43
76
|
|
|
77
|
+
function emptyIs(): QPlatformIs {
|
|
78
|
+
return {
|
|
79
|
+
mobile: false,
|
|
80
|
+
tablet: false,
|
|
81
|
+
desktop: false,
|
|
82
|
+
nativeMobile: false,
|
|
83
|
+
nativeMobileWrapper: undefined,
|
|
84
|
+
ios: false,
|
|
85
|
+
android: false,
|
|
86
|
+
iphone: false,
|
|
87
|
+
ipad: false,
|
|
88
|
+
ipod: false,
|
|
89
|
+
mac: false,
|
|
90
|
+
win: false,
|
|
91
|
+
linux: false,
|
|
92
|
+
chrome: false,
|
|
93
|
+
edge: false,
|
|
94
|
+
firefox: false,
|
|
95
|
+
opera: false,
|
|
96
|
+
safari: false,
|
|
97
|
+
vivaldi: false,
|
|
98
|
+
ie: false,
|
|
99
|
+
webkit: false,
|
|
100
|
+
bex: false,
|
|
101
|
+
cros: false,
|
|
102
|
+
blackberry: false,
|
|
103
|
+
winphone: false,
|
|
104
|
+
silk: false,
|
|
105
|
+
cordova: false,
|
|
106
|
+
capacitor: false,
|
|
107
|
+
electron: false,
|
|
108
|
+
touch: false,
|
|
109
|
+
mouse: false,
|
|
110
|
+
name: "",
|
|
111
|
+
version: "",
|
|
112
|
+
versionNumber: -1,
|
|
113
|
+
platform: "",
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** Nom + version du navigateur depuis l'UA (ordre = spécificité) */
|
|
118
|
+
function detectBrowser(ua: string) {
|
|
119
|
+
const m = (re: RegExp) => ua.match(re)?.[1] ?? ""
|
|
120
|
+
let name = "generic"
|
|
121
|
+
let version = ""
|
|
122
|
+
|
|
123
|
+
if (/vivaldi\/([\d.]+)/i.test(ua)) {
|
|
124
|
+
name = "vivaldi"
|
|
125
|
+
version = m(/vivaldi\/([\d.]+)/i)
|
|
126
|
+
}
|
|
127
|
+
else if (/edg\/([\d.]+)/i.test(ua)) {
|
|
128
|
+
name = "edge"
|
|
129
|
+
version = m(/edg\/([\d.]+)/i)
|
|
130
|
+
}
|
|
131
|
+
else if (/edge\/([\d.]+)/i.test(ua)) {
|
|
132
|
+
name = "edge"
|
|
133
|
+
version = m(/edge\/([\d.]+)/i)
|
|
134
|
+
}
|
|
135
|
+
else if (/opr\/([\d.]+)/i.test(ua)) {
|
|
136
|
+
name = "opera"
|
|
137
|
+
version = m(/opr\/([\d.]+)/i)
|
|
138
|
+
}
|
|
139
|
+
else if (/opera\/([\d.]+)/i.test(ua)) {
|
|
140
|
+
name = "opera"
|
|
141
|
+
version = m(/opera\/([\d.]+)/i)
|
|
142
|
+
}
|
|
143
|
+
else if (/(?:chrome|crios)\/([\d.]+)/i.test(ua)) {
|
|
144
|
+
name = "chrome"
|
|
145
|
+
version = m(/(?:chrome|crios)\/([\d.]+)/i)
|
|
146
|
+
}
|
|
147
|
+
else if (/(?:firefox|fxios)\/([\d.]+)/i.test(ua)) {
|
|
148
|
+
name = "firefox"
|
|
149
|
+
version = m(/(?:firefox|fxios)\/([\d.]+)/i)
|
|
150
|
+
}
|
|
151
|
+
else if (/msie ([\d.]+)/i.test(ua)) {
|
|
152
|
+
name = "ie"
|
|
153
|
+
version = m(/msie ([\d.]+)/i)
|
|
154
|
+
}
|
|
155
|
+
else if (/trident\/.*rv:([\d.]+)/i.test(ua)) {
|
|
156
|
+
name = "ie"
|
|
157
|
+
version = m(/trident\/.*rv:([\d.]+)/i)
|
|
158
|
+
}
|
|
159
|
+
else if (/version\/([\d.]+)/i.test(ua)) {
|
|
160
|
+
name = "safari"
|
|
161
|
+
version = m(/version\/([\d.]+)/i)
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const versionNumber = version ? parseInt(version.split(".")[0]!, 10) || -1 : -1
|
|
165
|
+
return { name, version, versionNumber }
|
|
166
|
+
}
|
|
167
|
+
|
|
44
168
|
function detect(): QPlatform {
|
|
45
169
|
if (typeof window === "undefined" || typeof navigator === "undefined") {
|
|
46
|
-
const empty = Object.fromEntries(
|
|
47
|
-
[
|
|
48
|
-
"mobile", "tablet", "desktop", "nativeMobile", "ios", "android", "iphone",
|
|
49
|
-
"ipad", "ipod", "mac", "win", "linux", "chrome", "edge", "firefox",
|
|
50
|
-
"opera", "safari", "ie", "cordova", "capacitor", "electron", "touch", "mouse",
|
|
51
|
-
].map((k) => [k, false]),
|
|
52
|
-
) as unknown as QPlatformIs
|
|
53
|
-
|
|
54
170
|
return {
|
|
55
171
|
userAgent: "",
|
|
56
172
|
platform: "",
|
|
57
173
|
isServer: true,
|
|
58
174
|
isClient: false,
|
|
59
|
-
is:
|
|
175
|
+
is: emptyIs(),
|
|
176
|
+
has: { touch: false, webStorage: false },
|
|
60
177
|
within: { iframe: false },
|
|
61
178
|
}
|
|
62
179
|
}
|
|
@@ -71,17 +188,54 @@ function detect(): QPlatform {
|
|
|
71
188
|
// iPad modernes : MacIntel + écran tactile multi-points
|
|
72
189
|
const ipad = /ipad/i.test(ua) || (/macintosh|macintel/i.test(platform) && (nav.maxTouchPoints ?? 0) > 1)
|
|
73
190
|
const android = /android/i.test(ua)
|
|
191
|
+
const blackberry = /blackberry/i.test(ua)
|
|
192
|
+
const winphone = /windows phone/i.test(ua)
|
|
193
|
+
// Chromebooks : navigator.platform vaut "CrOS x86_64" (ou "Linux x86_64" ancien)
|
|
194
|
+
const cros = /cros/i.test(platform)
|
|
195
|
+
const silk = /silk/i.test(ua)
|
|
74
196
|
|
|
75
197
|
const touch = "ontouchstart" in window || (nav.maxTouchPoints ?? 0) > 0
|
|
76
198
|
const mobileUA = /mobile|iphone|ipod|android|windows phone|blackberry/i.test(ua)
|
|
77
199
|
const tablet = ipad || (android && !mobileUA)
|
|
78
200
|
const mobile = mobileUA || (tablet ? false : touch && /android|ipad|iphone/i.test(ua))
|
|
79
201
|
|
|
202
|
+
const cordova = "cordova" in window
|
|
203
|
+
const capacitor = !!(window as any).Capacitor?.isNativePlatform?.()
|
|
204
|
+
const nativeMobileWrapper: "cordova" | "capacitor" | undefined = capacitor
|
|
205
|
+
? "capacitor"
|
|
206
|
+
: cordova
|
|
207
|
+
? "cordova"
|
|
208
|
+
: undefined
|
|
209
|
+
|
|
210
|
+
// nom de l'OS (miroir de l'API Quasar : is.platform)
|
|
211
|
+
let osName = "generic"
|
|
212
|
+
if (android) osName = "android"
|
|
213
|
+
else if (ios) osName = "ios"
|
|
214
|
+
else if (blackberry) osName = "blackberry"
|
|
215
|
+
else if (winphone) osName = "winphone"
|
|
216
|
+
else if (cros) osName = "cros"
|
|
217
|
+
else if (silk) osName = "silk"
|
|
218
|
+
else if (/mac|iphone|ipad|ipod/i.test(ua)) osName = "mac"
|
|
219
|
+
else if (/win/i.test(ua)) osName = "win"
|
|
220
|
+
else if (/linux/i.test(platform)) osName = "linux"
|
|
221
|
+
|
|
222
|
+
let webStorage = false
|
|
223
|
+
try {
|
|
224
|
+
webStorage = typeof window.localStorage !== "undefined"
|
|
225
|
+
}
|
|
226
|
+
catch {
|
|
227
|
+
webStorage = false
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const browser = detectBrowser(ua)
|
|
231
|
+
const notChromium = !/(edg|opr|vivaldi)/i.test(ua)
|
|
232
|
+
|
|
80
233
|
const is: QPlatformIs = {
|
|
81
234
|
mobile,
|
|
82
235
|
tablet,
|
|
83
236
|
desktop: !mobileUA && !tablet,
|
|
84
|
-
nativeMobile:
|
|
237
|
+
nativeMobile: mobile && (capacitor || cordova),
|
|
238
|
+
nativeMobileWrapper,
|
|
85
239
|
ios,
|
|
86
240
|
android,
|
|
87
241
|
iphone,
|
|
@@ -90,28 +244,40 @@ function detect(): QPlatform {
|
|
|
90
244
|
mac: /mac|iphone|ipad|ipod/i.test(ua),
|
|
91
245
|
win: /win/i.test(ua),
|
|
92
246
|
linux: /linux/i.test(platform),
|
|
93
|
-
chrome: /chrome|crios/i.test(ua) &&
|
|
94
|
-
edge: /edg/i.test(ua),
|
|
247
|
+
chrome: /chrome|crios/i.test(ua) && notChromium,
|
|
248
|
+
edge: /edg|edge/i.test(ua),
|
|
95
249
|
firefox: /firefox|fxios/i.test(ua),
|
|
96
250
|
opera: /opr|opera/i.test(ua),
|
|
97
|
-
safari: /safari/i.test(ua) && !/chrome|crios
|
|
251
|
+
safari: /safari/i.test(ua) && notChromium && !/chrome|crios/i.test(ua),
|
|
252
|
+
vivaldi: /vivaldi/i.test(ua),
|
|
98
253
|
ie: /msie|trident/i.test(ua),
|
|
99
|
-
|
|
100
|
-
|
|
254
|
+
webkit: /applewebkit/i.test(ua),
|
|
255
|
+
bex:
|
|
256
|
+
typeof window !== "undefined" &&
|
|
257
|
+
!!((window as any).chrome?.runtime?.id || (globalThis as any).browser?.runtime?.id),
|
|
258
|
+
cros,
|
|
259
|
+
blackberry,
|
|
260
|
+
winphone,
|
|
261
|
+
silk,
|
|
262
|
+
cordova,
|
|
263
|
+
capacitor,
|
|
101
264
|
electron: /electron/i.test(ua),
|
|
102
265
|
touch,
|
|
103
|
-
mouse:
|
|
266
|
+
mouse:
|
|
267
|
+
typeof window.matchMedia === "function" && window.matchMedia("(pointer: fine)").matches,
|
|
268
|
+
name: browser.name,
|
|
269
|
+
version: browser.version,
|
|
270
|
+
versionNumber: browser.versionNumber,
|
|
271
|
+
platform: osName,
|
|
104
272
|
}
|
|
105
273
|
|
|
106
|
-
// nativeMobile : mobile + Capacitor/Cordova
|
|
107
|
-
is.nativeMobile = is.mobile && (is.capacitor || is.cordova)
|
|
108
|
-
|
|
109
274
|
return {
|
|
110
275
|
userAgent: ua,
|
|
111
276
|
platform,
|
|
112
277
|
isServer: false,
|
|
113
278
|
isClient: true,
|
|
114
279
|
is,
|
|
280
|
+
has: { touch, webStorage },
|
|
115
281
|
within: { iframe: window.self !== window.top },
|
|
116
282
|
}
|
|
117
283
|
}
|
package/lib/q.ts
CHANGED
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
// app.use(QPlugin) → accès $q via this.$q / getCurrentInstance().proxy.$q
|
|
6
6
|
// ou import direct :
|
|
7
7
|
// import { $q } from "@dnax/ui" → $q.dialog.open(...)
|
|
8
|
-
import { ref } from "vue"
|
|
8
|
+
import { inject, ref } from "vue"
|
|
9
9
|
import { h } from "vue"
|
|
10
|
-
import type { App, Component, Ref } from "vue"
|
|
10
|
+
import type { App, Component, InjectionKey, Ref } from "vue"
|
|
11
11
|
import { toast } from "vue-sonner"
|
|
12
12
|
import type { ExternalToast } from "vue-sonner"
|
|
13
13
|
import QNotifyToast from "../components/QNotifyToast.vue"
|
|
@@ -44,6 +44,39 @@ export interface DialogController {
|
|
|
44
44
|
_opts: DialogOptions
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
+
// — Contexte plugin dialog (pattern Quasar) —
|
|
48
|
+
// Le composant passé à $q.dialog.open({ component }) DOIT contenir un
|
|
49
|
+
// <q-dialog v-model="open" @hide="onDialogHide"> à sa racine ; il pilote
|
|
50
|
+
// l'ouverture via le composable useDialogPluginComponent().
|
|
51
|
+
export interface DialogPluginContext {
|
|
52
|
+
/** Ref d'ouverture (v-model du <q-dialog> du composant) */
|
|
53
|
+
open: Ref<boolean>
|
|
54
|
+
/** Ferme en validant — le résolveur onOK est appelé */
|
|
55
|
+
onOK: (data?: unknown) => void
|
|
56
|
+
/** Ferme en annulant — le résolveur onCancel est appelé */
|
|
57
|
+
onCancel: () => void
|
|
58
|
+
/** Fermé par backdrop / Échap / × — le résolveur onDismiss est appelé */
|
|
59
|
+
onHide: () => void
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export const qDialogPluginKey: InjectionKey<DialogPluginContext> = Symbol("q-dialog-plugin")
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Composable Quasar-style pour les composants passés à $q.dialog.open().
|
|
66
|
+
* Le composant doit rendre un <q-dialog v-model="open" @hide="onDialogHide">
|
|
67
|
+
* comme racine. Expose aussi onDialogOK / onDialogCancel pour fermer en validant
|
|
68
|
+
* ou en annulant, et onDialogHide à brancher sur @hide du q-dialog.
|
|
69
|
+
*/
|
|
70
|
+
export function useDialogPluginComponent() {
|
|
71
|
+
const ctx = inject(qDialogPluginKey, null)
|
|
72
|
+
return {
|
|
73
|
+
open: ctx?.open ?? ref(false),
|
|
74
|
+
onDialogHide: () => ctx?.onHide(),
|
|
75
|
+
onDialogOK: (data?: unknown) => ctx?.onOK(data),
|
|
76
|
+
onDialogCancel: () => ctx?.onCancel(),
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
47
80
|
// — Pile de dialogues (singleton module-level, client-side) —
|
|
48
81
|
const dialogs: Ref<DialogController[]> = ref([])
|
|
49
82
|
|
package/package.json
CHANGED
|
@@ -26,7 +26,7 @@ const exports = files
|
|
|
26
26
|
|
|
27
27
|
// Exports manuels (composables / helpers) — conservés à chaque régénération
|
|
28
28
|
const manualExports = `
|
|
29
|
-
export { $q, usePlugin, useQ, QPlugin, dialogStack, closeDialog, bottomSheetStack, closeBottomSheet } from "./lib/q"
|
|
29
|
+
export { $q, usePlugin, useQ, QPlugin, dialogStack, closeDialog, bottomSheetStack, closeBottomSheet, useDialogPluginComponent } from "./lib/q"
|
|
30
30
|
export type {
|
|
31
31
|
DialogOptions,
|
|
32
32
|
DialogController,
|