@dnax/ui 0.0.12 → 0.0.13

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.
@@ -2,11 +2,24 @@
2
2
  // QBottomSheetFooter — pied du panneau façon barre d'app (équivalent QFooter) :
3
3
  // les actions sont embarquées dans un <q-toolbar> (min-height 50px, padding
4
4
  // 0 12px) et alignées à droite (justify-content: flex-end — styles/main.css).
5
+ // no-padding retire le padding horizontal du toolbar.
5
6
  import QToolbar from "./QToolbar.vue"
7
+
8
+ interface Props {
9
+ /** Supprime le padding du toolbar (actions collées aux bords) */
10
+ noPadding?: boolean
11
+ }
12
+
13
+ withDefaults(defineProps<Props>(), {
14
+ noPadding: false,
15
+ })
6
16
  </script>
7
17
 
8
18
  <template>
9
- <div class="q-bottom-sheet__footer">
19
+ <div
20
+ class="q-bottom-sheet__footer"
21
+ :class="{ 'q-bottom-sheet__footer--no-padding': noPadding }"
22
+ >
10
23
  <q-toolbar>
11
24
  <slot />
12
25
  </q-toolbar>
@@ -2,6 +2,7 @@
2
2
  // QBottomSheetHeader — header du panneau façon barre d'app (équivalent QHeader) :
3
3
  // le contenu est embarqué dans un <q-toolbar> (min-height 50px, padding 0 12px),
4
4
  // avec titre + description + bouton fermer (équivalent DrawerHeader/DrawerTitle).
5
+ // no-padding retire le padding horizontal du toolbar.
5
6
  import { inject } from "vue"
6
7
  import { Icon } from "@iconify/vue"
7
8
  import { icons } from "../lib/icons"
@@ -12,14 +13,22 @@ import QSpace from "./QSpace.vue"
12
13
  interface Props {
13
14
  title?: string
14
15
  description?: string
16
+ /** Supprime le padding du toolbar (contenu collé aux bords) */
17
+ noPadding?: boolean
15
18
  }
16
19
 
17
- const props = defineProps<Props>()
20
+ withDefaults(defineProps<Props>(), {
21
+ noPadding: false,
22
+ })
23
+
18
24
  const sheet = inject(qBottomSheetKey, null)
19
25
  </script>
20
26
 
21
27
  <template>
22
- <div class="q-bottom-sheet__header">
28
+ <div
29
+ class="q-bottom-sheet__header"
30
+ :class="{ 'q-bottom-sheet__header--no-padding': noPadding }"
31
+ >
23
32
  <q-toolbar>
24
33
  <div class="q-bottom-sheet__header-text">
25
34
  <h2 v-if="title" class="q-bottom-sheet__title">{{ title }}</h2>
@@ -88,8 +88,36 @@ const onDocKeydown = (e: KeyboardEvent) => {
88
88
  // « Retour » navigateur → ferme le dialog au lieu de naviguer
89
89
  useOverlayBack(open, () => { open.value = false }, "QDialog")
90
90
 
91
- watch(open, (v) => {
92
- if (typeof document !== "undefined") document.body.style.overflow = v ? "hidden" : ""
91
+ // — Verrou du scroll de la page derrière le dialog —
92
+ // Compteur module-level + suivi par instance : chaque dialog verrouille au plus
93
+ // une fois et déverrouille au plus une fois → pile de dialogs ($q.dialog) sans
94
+ // déverrouillage prématuré. Verrouille html + body (iOS Safari ignore
95
+ // overflow:hidden sur body seul).
96
+ let scrollLockCount = 0
97
+ function setBodyScrollLock(locked: boolean) {
98
+ if (typeof document === "undefined") return
99
+ scrollLockCount = Math.max(0, scrollLockCount + (locked ? 1 : -1))
100
+ const on = scrollLockCount > 0
101
+ document.documentElement.style.overflow = on ? "hidden" : ""
102
+ document.body.style.overflow = on ? "hidden" : ""
103
+ }
104
+
105
+ let didLock = false
106
+ const lockIf = (v: boolean) => {
107
+ if (v && !didLock) {
108
+ didLock = true
109
+ setBodyScrollLock(true)
110
+ }
111
+ else if (!v && didLock) {
112
+ didLock = false
113
+ setBodyScrollLock(false)
114
+ }
115
+ }
116
+
117
+ onMounted(() => lockIf(props.modelValue))
118
+ watch(open, lockIf)
119
+ onBeforeUnmount(() => {
120
+ if (didLock) setBodyScrollLock(false)
93
121
  })
94
122
 
95
123
  onMounted(() => {
@@ -98,8 +126,8 @@ onMounted(() => {
98
126
  onBeforeUnmount(() => {
99
127
  if (typeof document !== "undefined") {
100
128
  document.removeEventListener("keydown", onDocKeydown)
101
- document.body.style.overflow = ""
102
129
  }
130
+ if (didLock) setBodyScrollLock(false)
103
131
  })
104
132
 
105
133
  const overlayClasses = computed(() => cn("q-dialog__overlay", `q-dialog__overlay--${props.position}`))
@@ -2,11 +2,24 @@
2
2
  // QDialogFooter — pied de la modale façon barre d'app (équivalent QFooter) :
3
3
  // les actions sont embarquées dans un <q-toolbar> (min-height 50px, padding
4
4
  // 0 12px) et alignées à droite (justify-content: flex-end — styles/main.css).
5
+ // no-padding retire le padding horizontal du toolbar.
5
6
  import QToolbar from "./QToolbar.vue"
7
+
8
+ interface Props {
9
+ /** Supprime le padding du toolbar (actions collées aux bords) */
10
+ noPadding?: boolean
11
+ }
12
+
13
+ withDefaults(defineProps<Props>(), {
14
+ noPadding: false,
15
+ })
6
16
  </script>
7
17
 
8
18
  <template>
9
- <div class="q-dialog__footer">
19
+ <div
20
+ class="q-dialog__footer"
21
+ :class="{ 'q-dialog__footer--no-padding': noPadding }"
22
+ >
10
23
  <q-toolbar>
11
24
  <slot />
12
25
  </q-toolbar>
@@ -2,6 +2,7 @@
2
2
  // QDialogHeader — header de la modale façon barre d'app (équivalent QHeader) :
3
3
  // le contenu est embarqué dans un <q-toolbar> (min-height 50px, padding 0 12px),
4
4
  // avec titre + description + bouton fermer (équivalent DialogHeader/Title/Close).
5
+ // no-padding retire le padding horizontal du toolbar.
5
6
  import { inject } from "vue"
6
7
  import { Icon } from "@iconify/vue"
7
8
  import { icons } from "../lib/icons"
@@ -14,17 +15,23 @@ interface Props {
14
15
  description?: string
15
16
  /** Bouton fermer */
16
17
  showClose?: boolean
18
+ /** Supprime le padding du toolbar (contenu collé aux bords) */
19
+ noPadding?: boolean
17
20
  }
18
21
 
19
22
  const props = withDefaults(defineProps<Props>(), {
20
23
  showClose: true,
24
+ noPadding: false,
21
25
  })
22
26
 
23
27
  const dialog = inject(qDialogKey, null)
24
28
  </script>
25
29
 
26
30
  <template>
27
- <div class="q-dialog__header">
31
+ <div
32
+ class="q-dialog__header"
33
+ :class="{ 'q-dialog__header--no-padding': noPadding }"
34
+ >
28
35
  <q-toolbar>
29
36
  <div class="q-dialog__header-text">
30
37
  <h2 v-if="title" class="q-dialog__title">{{ title }}</h2>
@@ -16,6 +16,8 @@ interface Props {
16
16
  translucent?: boolean | number
17
17
  /** Glassmorphism marqué : fond très translucide + flou fort + bordure claire */
18
18
  glass?: boolean
19
+ /** Supprime le padding horizontal (éléments collés aux bords, safe-area conservée) */
20
+ noPadding?: boolean
19
21
  }
20
22
 
21
23
  const props = withDefaults(defineProps<Props>(), {
@@ -23,6 +25,7 @@ const props = withDefaults(defineProps<Props>(), {
23
25
  elevated: false,
24
26
  reveal: false,
25
27
  bordered: false,
28
+ noPadding: false,
26
29
  })
27
30
 
28
31
  const rootEl = ref<HTMLElement | null>(null)
@@ -71,6 +74,7 @@ onBeforeUnmount(() => {
71
74
  'q-footer--reveal-hidden': props.reveal && !visible,
72
75
  'q-footer--translucent': props.translucent === true || typeof props.translucent === 'number',
73
76
  'q-footer--glass': props.glass,
77
+ 'q-footer--no-padding': props.noPadding,
74
78
  }"
75
79
  :style="translucentStyle"
76
80
  >
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dnax/ui",
3
- "version": "0.0.12",
3
+ "version": "0.0.13",
4
4
  "module": "index.ts",
5
5
  "description": "UI design System",
6
6
  "type": "module",
package/styles/main.css CHANGED
@@ -582,6 +582,20 @@ html.dark body {
582
582
  z-index: 100;
583
583
  }
584
584
 
585
+ /* no-padding : horizontal collé aux bords — safe-area conservées */
586
+ .q-footer--no-padding {
587
+ padding: 4px 0;
588
+ padding-bottom: 0;
589
+ padding-bottom: constant(safe-area-inset-bottom);
590
+ padding-bottom: env(safe-area-inset-bottom);
591
+ padding-left: 0;
592
+ padding-left: constant(safe-area-inset-left);
593
+ padding-left: env(safe-area-inset-left);
594
+ padding-right: 0;
595
+ padding-right: constant(safe-area-inset-right);
596
+ padding-right: env(safe-area-inset-right);
597
+ }
598
+
585
599
  .q-footer--elevated {
586
600
  box-shadow: 0 -2px 4px rgb(0 0 0 / 0.2);
587
601
  }
@@ -4644,6 +4658,12 @@ html.dark body {
4644
4658
  justify-content: flex-end; /* actions alignées à droite (défaut) */
4645
4659
  }
4646
4660
 
4661
+ .q-bottom-sheet__footer--no-padding .q-toolbar,
4662
+ .q-bottom-sheet__header--no-padding .q-toolbar,
4663
+ .q-dialog__header--no-padding .q-toolbar {
4664
+ padding: 0; /* no-padding : contenu collé aux bords */
4665
+ }
4666
+
4647
4667
  .q-bottom-sheet__panel--dark .q-bottom-sheet__header,
4648
4668
  .q-bottom-sheet__panel--dark .q-bottom-sheet__footer {
4649
4669
  border-color: rgb(255 255 255 / 0.12);
@@ -5847,6 +5867,8 @@ html.dark body {
5847
5867
  max-width: 90vw;
5848
5868
  max-height: 90vh;
5849
5869
  overflow: auto;
5870
+ /* Le scroll du contenu ne doit jamais se chaîner à la page derrière */
5871
+ overscroll-behavior: contain;
5850
5872
  background-color: #fff;
5851
5873
  color: inherit;
5852
5874
  border-radius: var(--q-radius);
@@ -6014,6 +6036,10 @@ html.dark body {
6014
6036
  justify-content: flex-end; /* actions alignées à droite (défaut) */
6015
6037
  }
6016
6038
 
6039
+ .q-dialog__footer--no-padding .q-toolbar {
6040
+ padding: 0; /* no-padding : actions collées aux bords */
6041
+ }
6042
+
6017
6043
  .q-dialog__body {
6018
6044
  min-height: 0;
6019
6045
  padding: 4px 16px 16px;