@dnax/ui 0.0.1

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.
Files changed (150) hide show
  1. package/README.md +15 -0
  2. package/components/QAccordion.vue +68 -0
  3. package/components/QAccordionContent.vue +62 -0
  4. package/components/QAccordionItem.vue +24 -0
  5. package/components/QAccordionTrigger.vue +51 -0
  6. package/components/QActionSheet.vue +159 -0
  7. package/components/QApp.vue +8 -0
  8. package/components/QAutocomplete.vue +316 -0
  9. package/components/QAvatar.vue +92 -0
  10. package/components/QBackHeader.vue +69 -0
  11. package/components/QBackTop.vue +125 -0
  12. package/components/QBadge.vue +55 -0
  13. package/components/QBottomSheet.vue +177 -0
  14. package/components/QBottomSheetFooter.vue +9 -0
  15. package/components/QBottomSheetHeader.vue +35 -0
  16. package/components/QBottomSheetProvider.vue +62 -0
  17. package/components/QBottomSheetTrigger.vue +18 -0
  18. package/components/QBtn.vue +144 -0
  19. package/components/QBtnGroup.vue +31 -0
  20. package/components/QBubble.vue +36 -0
  21. package/components/QBubbleContent.vue +28 -0
  22. package/components/QBubbleGroup.vue +25 -0
  23. package/components/QBubbleReactions.vue +40 -0
  24. package/components/QCard.vue +63 -0
  25. package/components/QCardActions.vue +27 -0
  26. package/components/QCardContent.vue +29 -0
  27. package/components/QCarousel.vue +115 -0
  28. package/components/QCarouselContent.vue +23 -0
  29. package/components/QCarouselItem.vue +20 -0
  30. package/components/QCarouselNav.vue +51 -0
  31. package/components/QCheckbox.vue +146 -0
  32. package/components/QChip.vue +91 -0
  33. package/components/QCircularProgress.vue +122 -0
  34. package/components/QCol.vue +52 -0
  35. package/components/QCollapse.vue +141 -0
  36. package/components/QConfigProvider.vue +164 -0
  37. package/components/QContainer.vue +201 -0
  38. package/components/QCountDown.vue +164 -0
  39. package/components/QCountryPicker.vue +593 -0
  40. package/components/QDataGrid.vue +491 -0
  41. package/components/QDatePicker.vue +250 -0
  42. package/components/QDialog.vue +163 -0
  43. package/components/QDialogFooter.vue +9 -0
  44. package/components/QDialogHeader.vue +41 -0
  45. package/components/QDialogProvider.vue +65 -0
  46. package/components/QDialogTrigger.vue +18 -0
  47. package/components/QFab.vue +80 -0
  48. package/components/QFabAction.vue +48 -0
  49. package/components/QFilePicker.vue +294 -0
  50. package/components/QFooter.vue +72 -0
  51. package/components/QGallery.vue +280 -0
  52. package/components/QGrid.vue +54 -0
  53. package/components/QHeader.vue +38 -0
  54. package/components/QIcon.vue +53 -0
  55. package/components/QImagePicker.vue +248 -0
  56. package/components/QImagePreview.vue +225 -0
  57. package/components/QImagePreviewProvider.vue +35 -0
  58. package/components/QImg.vue +142 -0
  59. package/components/QInfiniteScroll.vue +109 -0
  60. package/components/QInnerLoading.vue +67 -0
  61. package/components/QInput.vue +289 -0
  62. package/components/QInputOtp.vue +238 -0
  63. package/components/QInputPassword.vue +67 -0
  64. package/components/QInputTag.vue +281 -0
  65. package/components/QIntersection.vue +119 -0
  66. package/components/QItem.vue +60 -0
  67. package/components/QItemSection.vue +47 -0
  68. package/components/QLinearProgress.vue +94 -0
  69. package/components/QList.vue +39 -0
  70. package/components/QLoading.vue +77 -0
  71. package/components/QLoadingProvider.vue +41 -0
  72. package/components/QMarquee.vue +56 -0
  73. package/components/QMessageScroller.vue +10 -0
  74. package/components/QMessageScrollerButton.vue +48 -0
  75. package/components/QMessageScrollerContent.vue +31 -0
  76. package/components/QMessageScrollerItem.vue +49 -0
  77. package/components/QMessageScrollerProvider.vue +198 -0
  78. package/components/QMessageScrollerViewport.vue +109 -0
  79. package/components/QNavMenu.vue +75 -0
  80. package/components/QNavMenuContent.vue +10 -0
  81. package/components/QNavMenuItem.vue +60 -0
  82. package/components/QNavMenuTrigger.vue +96 -0
  83. package/components/QNotifyProvider.vue +33 -0
  84. package/components/QNotifyToast.vue +60 -0
  85. package/components/QPage.vue +56 -0
  86. package/components/QPagination.vue +195 -0
  87. package/components/QParallax.vue +124 -0
  88. package/components/QPullToRefresh.vue +138 -0
  89. package/components/QRadio.vue +99 -0
  90. package/components/QRating.vue +105 -0
  91. package/components/QReorder.vue +191 -0
  92. package/components/QRollingText.vue +225 -0
  93. package/components/QRow.vue +23 -0
  94. package/components/QSafeArea.vue +39 -0
  95. package/components/QScrollArea.vue +118 -0
  96. package/components/QSelect.vue +698 -0
  97. package/components/QSeparator.vue +39 -0
  98. package/components/QSidebar.vue +153 -0
  99. package/components/QSidebarContent.vue +14 -0
  100. package/components/QSidebarFooter.vue +14 -0
  101. package/components/QSidebarHeader.vue +14 -0
  102. package/components/QSidebarMenu.vue +9 -0
  103. package/components/QSidebarMenuButton.vue +59 -0
  104. package/components/QSidebarMenuItem.vue +9 -0
  105. package/components/QSidebarTrigger.vue +32 -0
  106. package/components/QSkeleton.vue +95 -0
  107. package/components/QSlider.vue +205 -0
  108. package/components/QSpace.vue +10 -0
  109. package/components/QSpinner.vue +172 -0
  110. package/components/QSplitter.vue +145 -0
  111. package/components/QSticky.vue +181 -0
  112. package/components/QSwipeCell.vue +217 -0
  113. package/components/QSwiper.vue +102 -0
  114. package/components/QSwiperSlide.vue +19 -0
  115. package/components/QSyntax.vue +171 -0
  116. package/components/QTab.vue +175 -0
  117. package/components/QTabPanel.vue +50 -0
  118. package/components/QTabPanels.vue +106 -0
  119. package/components/QTable.vue +575 -0
  120. package/components/QTabs.vue +188 -0
  121. package/components/QText.vue +316 -0
  122. package/components/QTextCaption.vue +122 -0
  123. package/components/QToolbar.vue +28 -0
  124. package/components/QTooltip.vue +313 -0
  125. package/components/QUploader.vue +473 -0
  126. package/components/QVideo.vue +289 -0
  127. package/components/QVirtualScroll.vue +190 -0
  128. package/components/internal/QDateCalendar.vue +153 -0
  129. package/index.ts +151 -0
  130. package/lib/breakpoints.ts +44 -0
  131. package/lib/colors.ts +24 -0
  132. package/lib/config.ts +34 -0
  133. package/lib/countries.ts +261 -0
  134. package/lib/formula.ts +630 -0
  135. package/lib/icons.ts +38 -0
  136. package/lib/loading.ts +75 -0
  137. package/lib/overlayBack.ts +124 -0
  138. package/lib/platform.ts +146 -0
  139. package/lib/primitive.ts +25 -0
  140. package/lib/q.ts +287 -0
  141. package/lib/screen.ts +105 -0
  142. package/lib/search.ts +35 -0
  143. package/lib/themeVars.ts +22 -0
  144. package/lib/useComponentProps.ts +54 -0
  145. package/lib/utils.ts +7 -0
  146. package/module.ts +89 -0
  147. package/package.json +41 -0
  148. package/scripts/generate-exports.ts +56 -0
  149. package/styles/main.css +7627 -0
  150. package/tsconfig.json +33 -0
@@ -0,0 +1,313 @@
1
+ <script setup lang="ts">
2
+ // QTooltip — API Quasar, comportement type shadcn-vue (reka-ui) :
3
+ // <q-tooltip side="top" align="center" :side-offset="8" show-arrow :delay="300" disable>
4
+ // À placer en enfant de la cible. Affichage au hover/focus, position fixed calculée
5
+ // avec FLIP automatique (bascule du côté opposé si manque de place) et clamping.
6
+ import { computed, getCurrentInstance, nextTick, onBeforeUnmount, onMounted, ref, watch } from "vue"
7
+ import { Icon } from "@iconify/vue"
8
+ import { cn } from "../lib/utils"
9
+ import { colorValue, foregroundFor } from "../lib/colors"
10
+
11
+ type Side = "top" | "bottom" | "left" | "right"
12
+ type Align = "start" | "center" | "end"
13
+ type TooltipType = "positive" | "success" | "negative" | "error" | "info" | "warning"
14
+
15
+ interface Props {
16
+ /** Contrôle externe (v-model) ; sinon géré au hover */
17
+ modelValue?: boolean
18
+ /** Délai d'ouverture (ms) — défaut 300 */
19
+ delay?: number
20
+ /** Délai d'ouverture (ms), prioritaire sur delay */
21
+ openDelay?: number
22
+ /** Délai de fermeture (ms) — défaut 0 */
23
+ closeDelay?: number
24
+ /** Désactive le tooltip */
25
+ disable?: boolean
26
+ /** Ancrage legacy : "edge align" (ex. "top middle", "right middle") */
27
+ anchor?: string
28
+ /** Bord d'ancrage : top | bottom | left | right (défaut : top) */
29
+ side?: Side
30
+ /** Alignement : start | center | end (défaut : center) */
31
+ align?: Align
32
+ /** Distance tooltip ↔ cible (px) — défaut 8 */
33
+ sideOffset?: number
34
+ /** Décalage d'alignement (px) — défaut 0 */
35
+ alignOffset?: number
36
+ /** Décalage supplémentaire [x, y] en px (compat ancêtre) */
37
+ offset?: [number, number]
38
+ /** Flèche pointant vers la cible */
39
+ showArrow?: boolean
40
+ /** true = le survol du tooltip ne le maintient pas ouvert (pointer-events none) */
41
+ disableHoverableContent?: boolean
42
+ /** Variante sémantique — définit le fond : positive/success, negative/error, info, warning */
43
+ type?: TooltipType
44
+ /** Couleur de fond (token ou hex) — prioritaire sur type */
45
+ color?: string
46
+ /** Couleur du texte (token ou hex) — défaut : blanc (contraste calculé si couleur libre) */
47
+ textColor?: string
48
+ /** Icône Iconify affichée dans le tooltip (ex. "lucide:info") */
49
+ icon?: string
50
+ /** Position de l'icône : left (défaut) | right */
51
+ iconPosition?: "left" | "right"
52
+ contentClass?: string
53
+ }
54
+
55
+ const props = withDefaults(defineProps<Props>(), {
56
+ disable: false,
57
+ sideOffset: 8,
58
+ alignOffset: 0,
59
+ offset: () => [0, 0] as [number, number],
60
+ showArrow: false,
61
+ disableHoverableContent: false,
62
+ contentClass: "",
63
+ })
64
+
65
+ const emit = defineEmits<{ "update:modelValue": [value: boolean] }>()
66
+
67
+ // — Délais —
68
+ const openDelay = computed(() => props.openDelay ?? props.delay ?? 300)
69
+ const closeDelay = computed(() => props.closeDelay ?? 0)
70
+
71
+ // — Side / align (props explicites > anchor legacy) —
72
+ const anchorSide = computed<Side>(() => (props.anchor?.split(" ")[0] as Side) || "top")
73
+ const anchorAlign = computed<Align>(() => {
74
+ const a = props.anchor?.split(" ")[1]
75
+ if (a === "left" || a === "top") return "start"
76
+ if (a === "right" || a === "bottom") return "end"
77
+ return "center"
78
+ })
79
+ const side = computed<Side>(() => props.side ?? anchorSide.value)
80
+ const align = computed<Align>(() => props.align ?? anchorAlign.value)
81
+ const effectiveSide = ref<Side>("top") // après flip éventuel
82
+
83
+ // — Ouverture contrôlée / interne —
84
+ // Détection du v-model par les PROPS RÉELLEMENT PASSÉES (vnode.props) : un prop
85
+ // Boolean non fourni vaut false (pas undefined) → `modelValue !== undefined`
86
+ // serait TOUJOURS contrôlé et le tooltip ne s'ouvrirait jamais sans v-model.
87
+ const instance = getCurrentInstance()
88
+ const controlled = computed(() => {
89
+ const p = instance?.vnode.props
90
+ return !!(p && ("modelValue" in p || "onUpdate:modelValue" in p))
91
+ })
92
+ const internalOpen = ref(false)
93
+ const open = computed(() => (controlled.value ? props.modelValue : internalOpen.value))
94
+
95
+ const setOpen = (v: boolean) => {
96
+ if (controlled.value) emit("update:modelValue", v)
97
+ else internalOpen.value = v
98
+ }
99
+
100
+ let showTimer: ReturnType<typeof setTimeout> | null = null
101
+ let hideTimer: ReturnType<typeof setTimeout> | null = null
102
+
103
+ const cancelHide = () => {
104
+ if (hideTimer) {
105
+ clearTimeout(hideTimer)
106
+ hideTimer = null
107
+ }
108
+ }
109
+
110
+ const show = () => {
111
+ if (props.disable) return
112
+ cancelHide()
113
+ if (showTimer) clearTimeout(showTimer)
114
+ showTimer = setTimeout(() => setOpen(true), openDelay.value)
115
+ }
116
+
117
+ const scheduleHide = () => {
118
+ if (showTimer) clearTimeout(showTimer)
119
+ if (hideTimer) clearTimeout(hideTimer)
120
+ hideTimer = setTimeout(() => setOpen(false), closeDelay.value)
121
+ }
122
+
123
+ // — Positionnement (flip + clamp) —
124
+ const tipRef = ref<HTMLElement | null>(null)
125
+ const parentEl = ref<HTMLElement | null>(null)
126
+ const posStyle = ref<Record<string, string>>({})
127
+ const arrowStyle = ref<Record<string, string>>({})
128
+ // Ancre invisible rendue DANS la cible : retrouve l'élément parent même après
129
+ // le Teleport vers body (qui couperait le lien DOM).
130
+ const anchorEl = ref<HTMLElement | null>(null)
131
+
132
+ const PAD = 8
133
+ const ARROW = 10
134
+
135
+ const updatePosition = () => {
136
+ const tip = tipRef.value
137
+ const parent = parentEl.value
138
+ if (!tip || !parent) return
139
+ const pr = parent.getBoundingClientRect()
140
+ const tw = tip.offsetWidth
141
+ const th = tip.offsetHeight
142
+ const vw = window.innerWidth
143
+ const vh = window.innerHeight
144
+
145
+ const place = (s: Side, a: Align) => {
146
+ const off = props.sideOffset
147
+ const aoff = props.alignOffset
148
+ let left = 0
149
+ let top = 0
150
+ if (s === "top" || s === "bottom") {
151
+ left = a === "start" ? pr.left + aoff : a === "end" ? pr.right - tw - aoff : pr.left + (pr.width - tw) / 2
152
+ top = s === "top" ? pr.top - th - off : pr.bottom + off
153
+ } else {
154
+ top = a === "start" ? pr.top + aoff : a === "end" ? pr.bottom - th - aoff : pr.top + (pr.height - th) / 2
155
+ left = s === "left" ? pr.left - tw - off : pr.right + off
156
+ }
157
+ return { left: left + props.offset[0], top: top + props.offset[1] }
158
+ }
159
+
160
+ let s = side.value
161
+ let a = align.value
162
+ let pos = place(s, a)
163
+
164
+ // FLIP : bascule sur le côté opposé si le côté demandé déborde
165
+ if (s === "top" && pos.top < PAD && pr.bottom + props.sideOffset + th <= vh - PAD) {
166
+ s = "bottom"
167
+ pos = place(s, a)
168
+ } else if (s === "bottom" && pos.top + th > vh - PAD && pr.top - props.sideOffset - th >= PAD) {
169
+ s = "top"
170
+ pos = place(s, a)
171
+ } else if (s === "left" && pos.left < PAD && pr.right + props.sideOffset + tw <= vw - PAD) {
172
+ s = "right"
173
+ pos = place(s, a)
174
+ } else if (s === "right" && pos.left + tw > vw - PAD && pr.left - props.sideOffset - tw >= PAD) {
175
+ s = "left"
176
+ pos = place(s, a)
177
+ }
178
+
179
+ // CLAMP : garde le tooltip dans le viewport
180
+ pos.left = Math.max(PAD, Math.min(pos.left, vw - tw - PAD))
181
+ pos.top = Math.max(PAD, Math.min(pos.top, vh - th - PAD))
182
+ effectiveSide.value = s
183
+
184
+ posStyle.value = { left: `${pos.left}px`, top: `${pos.top}px` }
185
+
186
+ // Flèche : centrée/alignée sur le bord côté cible
187
+ if (props.showArrow) {
188
+ if (s === "top" || s === "bottom") {
189
+ const x = a === "start" ? 14 + props.alignOffset : a === "end" ? tw - 14 - props.alignOffset : tw / 2
190
+ arrowStyle.value = {
191
+ left: `${x - ARROW / 2}px`,
192
+ top: s === "top" ? `${th - ARROW / 2}px` : `${-ARROW / 2}px`,
193
+ }
194
+ } else {
195
+ const y = a === "start" ? 14 + props.alignOffset : a === "end" ? th - 14 - props.alignOffset : th / 2
196
+ arrowStyle.value = {
197
+ top: `${y - ARROW / 2}px`,
198
+ left: s === "left" ? `${tw - ARROW / 2}px` : `${-ARROW / 2}px`,
199
+ }
200
+ }
201
+ }
202
+ }
203
+
204
+ watch(open, async (v) => {
205
+ if (v) {
206
+ await nextTick()
207
+ updatePosition()
208
+ if (typeof window !== "undefined") {
209
+ window.addEventListener("scroll", updatePosition, true)
210
+ window.addEventListener("resize", updatePosition)
211
+ }
212
+ } else if (typeof window !== "undefined") {
213
+ window.removeEventListener("scroll", updatePosition, true)
214
+ window.removeEventListener("resize", updatePosition)
215
+ }
216
+ })
217
+
218
+ onMounted(() => {
219
+ // L'ancre (span invisible rendu DANS la cible) permet de retrouver l'élément
220
+ // cible même après le Teleport vers body (qui coupe le lien DOM parent).
221
+ parentEl.value = anchorEl.value?.parentElement ?? null
222
+ const p = parentEl.value
223
+ if (p) {
224
+ p.addEventListener("mouseenter", show)
225
+ p.addEventListener("mouseleave", scheduleHide)
226
+ p.addEventListener("focusin", show)
227
+ p.addEventListener("focusout", scheduleHide)
228
+ }
229
+ })
230
+
231
+ onBeforeUnmount(() => {
232
+ const p = parentEl.value
233
+ if (p) {
234
+ p.removeEventListener("mouseenter", show)
235
+ p.removeEventListener("mouseleave", scheduleHide)
236
+ p.removeEventListener("focusin", show)
237
+ p.removeEventListener("focusout", scheduleHide)
238
+ }
239
+ if (typeof window !== "undefined") {
240
+ window.removeEventListener("scroll", updatePosition, true)
241
+ window.removeEventListener("resize", updatePosition)
242
+ }
243
+ if (showTimer) clearTimeout(showTimer)
244
+ if (hideTimer) clearTimeout(hideTimer)
245
+ })
246
+
247
+ const tipClasses = computed(() =>
248
+ cn("q-tooltip", open.value && "q-tooltip--show", props.contentClass),
249
+ )
250
+
251
+ // — Thème : type sémantique / couleur / texte / icône —
252
+ const typeBg = computed<string | undefined>(() => {
253
+ switch (props.type) {
254
+ case "positive":
255
+ case "success":
256
+ return "positive"
257
+ case "negative":
258
+ case "error":
259
+ return "negative"
260
+ case "info":
261
+ return "info"
262
+ case "warning":
263
+ return "warning"
264
+ default:
265
+ return undefined
266
+ }
267
+ })
268
+
269
+ const themeStyle = computed<Record<string, string>>(() => {
270
+ const s: Record<string, string> = {}
271
+ const bg = props.color ?? typeBg.value
272
+ if (bg) {
273
+ s["--q-tooltip-bg"] = colorValue(bg)
274
+ s["--q-tooltip-fg"] = props.textColor ? colorValue(props.textColor) : foregroundFor(bg)
275
+ } else if (props.textColor) {
276
+ s["--q-tooltip-fg"] = colorValue(props.textColor)
277
+ }
278
+ return s
279
+ })
280
+ </script>
281
+
282
+ <template>
283
+ <!-- Ancre invisible dans la cible : sert de référence pour le hover ET pour
284
+ retrouver l'élément cible après le Teleport. -->
285
+ <span ref="anchorEl" class="q-tooltip__anchor" aria-hidden="true" style="display: none" />
286
+ <Teleport to="body">
287
+ <div
288
+ ref="tipRef"
289
+ v-show="open"
290
+ class="q-tooltip"
291
+ :class="[tipClasses, disableHoverableContent && 'q-tooltip--no-hover']"
292
+ :style="[themeStyle, posStyle]"
293
+ role="tooltip"
294
+ @mouseenter="cancelHide"
295
+ @mouseleave="scheduleHide"
296
+ >
297
+ <Icon
298
+ v-if="icon && iconPosition !== 'right'"
299
+ :icon="icon"
300
+ class="q-tooltip__icon"
301
+ aria-hidden="true"
302
+ />
303
+ <slot />
304
+ <Icon
305
+ v-if="icon && iconPosition === 'right'"
306
+ :icon="icon"
307
+ class="q-tooltip__icon"
308
+ aria-hidden="true"
309
+ />
310
+ <span v-if="showArrow" class="q-tooltip__arrow" :style="arrowStyle" aria-hidden="true" />
311
+ </div>
312
+ </Teleport>
313
+ </template>