@dnax/ui 0.0.10 → 0.0.11

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.
@@ -40,8 +40,8 @@ interface Props {
40
40
  contentStyle?: StyleValue
41
41
  noBackdropDismiss?: boolean
42
42
  noEscDismiss?: boolean
43
- /** Animation : fade | zoom | slide-up | slide-down | slide-left | slide-right (sinon basée sur position) */
44
- transition?: "fade" | "zoom" | "slide-up" | "slide-down" | "slide-left" | "slide-right"
43
+ /** Animation : fade | zoom | slide-up | slide-down | slide-left | slide-right | swipe-left | swipe-right (sinon basée sur position) */
44
+ transition?: "fade" | "zoom" | "slide-up" | "slide-down" | "slide-left" | "slide-right" | "swipe-left" | "swipe-right"
45
45
  /** Durée des transitions d'entrée/sortie en ms (défaut CSS : ~200ms entrée, ~150ms sortie) */
46
46
  transitionDuration?: number
47
47
  }
@@ -2,26 +2,28 @@
2
2
  // QDialogProvider — rend la pile de dialogues programmatiques ($q.dialog).
3
3
  // Intégré dans QConfigProvider (rendu automatiquement par le plus externe),
4
4
  // mais utilisable aussi en autonome : <q-dialog-provider><slot /></q-dialog-provider>.
5
- // Chaque entrée de la pile est rendue dans un <q-dialog> ; quand le composant
6
- // émet ok / cancel / dismiss / close, le résolveur correspondant est appelé
5
+ //
6
+ // Pattern Quasar : le composant passé à $q.dialog.open({ component }) DOIT contenir
7
+ // un <q-dialog v-model="open" @hide="onDialogHide"> à sa racine, piloté via le
8
+ // composable useDialogPluginComponent() (exporté par @dnax/ui). Le provider fournit
9
+ // le contexte (open, onOK, onCancel) via QDialogHost ; à la fermeture (backdrop,
10
+ // Échap, ×, onDialogOK, onDialogCancel), le résolveur correspondant est appelé
7
11
  // puis l'entrée est retirée de la pile.
8
12
  import { computed, inject } from "vue"
9
13
  import { dialogStack, closeDialog } from "../lib/q"
10
14
  import type { DialogController } from "../lib/q"
11
15
  import { qConfigKey } from "../lib/config"
12
16
  import { themeVarsStyle } from "../lib/themeVars"
17
+ import QDialogHost from "./internal/QDialogHost.vue"
13
18
 
14
19
  const dialogs = dialogStack
15
20
 
16
21
  const config = inject(qConfigKey, null)
17
22
 
18
- // Les dialogs sont téléportés au body (QDialog) : on repose les tokens du thème
19
- // sur leur contenu pour qu'ils héritent des couleurs/radius du provider.
23
+ // Les dialogs sont téléportés au body (QDialog du composant passé) : on repose
24
+ // les tokens du thème sur le host pour qu'ils héritent des couleurs/radius du provider.
20
25
  const dialogThemeStyle = themeVarsStyle(computed(() => config?.theme.value ?? {}))
21
26
 
22
- const contentStyle = (ctrl: DialogController) =>
23
- typeof ctrl._opts.fullscreen === "string" ? { width: ctrl._opts.fullscreen } : undefined
24
-
25
27
  const handleOk = (ctrl: DialogController, data?: unknown) => {
26
28
  ctrl._resolvers.ok?.(data)
27
29
  closeDialog(ctrl)
@@ -38,28 +40,20 @@ const handleDismiss = (ctrl: DialogController) => {
38
40
 
39
41
  <template>
40
42
  <slot />
41
- <q-dialog
43
+ <div
42
44
  v-for="(dialog, idx) in dialogs"
43
45
  :key="idx"
44
- :model-value="true"
45
- :maximized="dialog._opts.fullscreen === true"
46
- :content-style="[contentStyle(dialog), dialogThemeStyle]"
47
- :content-class="dialog._opts.class"
48
- :persistent="dialog._opts.persistent"
49
- @update:model-value="(v: boolean) => !v && handleDismiss(dialog)"
46
+ :style="dialogThemeStyle"
50
47
  >
51
- <q-dialog-header
52
- v-if="dialog._opts.title || dialog._opts.description"
53
- :title="dialog._opts.title"
54
- :description="dialog._opts.description"
55
- />
56
- <component
57
- :is="dialog._opts.component"
58
- v-bind="dialog._opts.componentProps"
48
+ <q-dialog-host
49
+ :ctrl="dialog"
50
+ :on-ok="(data?: unknown) => handleOk(dialog, data)"
51
+ :on-cancel="() => handleCancel(dialog)"
52
+ :on-hide="() => handleDismiss(dialog)"
59
53
  @ok="(data?: unknown) => handleOk(dialog, data)"
60
- @cancel="handleCancel(dialog)"
61
- @dismiss="handleDismiss(dialog)"
62
- @close="handleDismiss(dialog)"
54
+ @cancel="() => handleCancel(dialog)"
55
+ @dismiss="() => handleDismiss(dialog)"
56
+ @close="() => handleDismiss(dialog)"
63
57
  />
64
- </q-dialog>
58
+ </div>
65
59
  </template>
@@ -0,0 +1,45 @@
1
+ <script setup lang="ts">
2
+ // QDialogHost — host interne du $q.dialog : rend le composant passé tel quel et
3
+ // lui fournit le contexte plugin (pattern Quasar). Le composant passé DOIT
4
+ // contenir un <q-dialog v-model="open" @hide="onDialogHide"> comme racine, piloté
5
+ // via useDialogPluginComponent(). À la fermeture (backdrop/Échap/× → @hide), ou
6
+ // via onDialogOK / onDialogCancel, les résolveurs sont appelés et l'entrée retirée.
7
+ import { provide, ref } from "vue"
8
+ import { qDialogPluginKey } from "../../lib/q"
9
+ import type { DialogController } from "../../lib/q"
10
+
11
+ const props = defineProps<{
12
+ ctrl: DialogController
13
+ onOk: (data?: unknown) => void
14
+ onCancel: () => void
15
+ onHide: () => void
16
+ }>()
17
+
18
+ const open = ref(true)
19
+
20
+ provide(qDialogPluginKey, {
21
+ open,
22
+ onOK: (data?: unknown) => props.onOk(data),
23
+ onCancel: () => props.onCancel(),
24
+ onHide: () => props.onHide(),
25
+ })
26
+
27
+ // Le composant émet aussi les events legacy (ok/cancel/dismiss/close)
28
+ const emit = defineEmits<{
29
+ ok: [data?: unknown]
30
+ cancel: []
31
+ dismiss: []
32
+ close: []
33
+ }>()
34
+ </script>
35
+
36
+ <template>
37
+ <component
38
+ :is="props.ctrl._opts.component"
39
+ v-bind="props.ctrl._opts.componentProps"
40
+ @ok="emit('ok', $event)"
41
+ @cancel="emit('cancel')"
42
+ @dismiss="emit('dismiss')"
43
+ @close="emit('close')"
44
+ />
45
+ </template>
package/index.ts CHANGED
@@ -126,7 +126,7 @@ export { default as QUploader } from "./components/QUploader.vue"
126
126
  export { default as QVideo } from "./components/QVideo.vue"
127
127
  export { default as QVirtualScroll } from "./components/QVirtualScroll.vue"
128
128
 
129
- export { $q, usePlugin, useQ, QPlugin, dialogStack, closeDialog, bottomSheetStack, closeBottomSheet } from "./lib/q"
129
+ export { $q, usePlugin, useQ, QPlugin, dialogStack, closeDialog, bottomSheetStack, closeBottomSheet, useDialogPluginComponent } from "./lib/q"
130
130
  export type {
131
131
  DialogOptions,
132
132
  DialogController,
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dnax/ui",
3
- "version": "0.0.10",
3
+ "version": "0.0.11",
4
4
  "module": "index.ts",
5
5
  "description": "UI design System",
6
6
  "type": "module",
@@ -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,
package/styles/main.css CHANGED
@@ -6237,6 +6237,58 @@ html.dark body {
6237
6237
  }
6238
6238
  }
6239
6239
 
6240
+ /* swipe-* : le panneau glisse depuis un bord et y REVIENT à la fermeture
6241
+ (aller-retour, comme la sidebar) — swipe-left : vient de la droite et y
6242
+ retourne ; swipe-right : vient de la gauche et y retourne. */
6243
+ .q-dialog-swipe-left-enter-active,
6244
+ .q-dialog-swipe-left-leave-active,
6245
+ .q-dialog-swipe-right-enter-active,
6246
+ .q-dialog-swipe-right-leave-active {
6247
+ transition: opacity var(--q-dialog-duration-enter, 0.2s) ease;
6248
+ }
6249
+
6250
+ .q-dialog-swipe-left-enter-active .q-dialog__content,
6251
+ .q-dialog-swipe-left-leave-active .q-dialog__content {
6252
+ --q-dialog-swipe-x: 100vw;
6253
+ }
6254
+
6255
+ .q-dialog-swipe-right-enter-active .q-dialog__content,
6256
+ .q-dialog-swipe-right-leave-active .q-dialog__content {
6257
+ --q-dialog-swipe-x: -100vw;
6258
+ }
6259
+
6260
+ .q-dialog-swipe-left-enter-active .q-dialog__content,
6261
+ .q-dialog-swipe-right-enter-active .q-dialog__content {
6262
+ animation: q-dialog-swipe-in var(--q-dialog-duration-enter, 0.3s) cubic-bezier(0.32, 0.72, 0, 1);
6263
+ }
6264
+
6265
+ .q-dialog-swipe-left-leave-active .q-dialog__content,
6266
+ .q-dialog-swipe-right-leave-active .q-dialog__content {
6267
+ animation: q-dialog-swipe-out var(--q-dialog-duration-leave, 0.2s) ease-in forwards;
6268
+ }
6269
+
6270
+ @keyframes q-dialog-swipe-in {
6271
+ from {
6272
+ opacity: 0.6;
6273
+ transform: translateX(var(--q-dialog-swipe-x, 100vw));
6274
+ }
6275
+ to {
6276
+ opacity: 1;
6277
+ transform: none;
6278
+ }
6279
+ }
6280
+
6281
+ @keyframes q-dialog-swipe-out {
6282
+ from {
6283
+ opacity: 1;
6284
+ transform: none;
6285
+ }
6286
+ to {
6287
+ opacity: 0;
6288
+ transform: translateX(var(--q-dialog-swipe-x, 100vw));
6289
+ }
6290
+ }
6291
+
6240
6292
  /* ===== QContainer ===== */
6241
6293
  .q-container {
6242
6294
  width: 100%;