@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
package/lib/screen.ts ADDED
@@ -0,0 +1,105 @@
1
+ // $q.screen — plugin Screen façon Quasar
2
+ // (https://quasar.dev/options/screen-plugin) : taille de fenêtre réactive,
3
+ // refs lt/gt, taille courante (name), seuils personnalisables (setSizes).
4
+ import { computed, reactive, ref, watch } from "vue"
5
+ import { useWindowSize } from "@vueuse/core"
6
+ import type { Ref } from "vue"
7
+
8
+ export interface QScreenSizes {
9
+ sm: number
10
+ md: number
11
+ lg: number
12
+ xl: number
13
+ }
14
+
15
+ export interface QScreen {
16
+ /** largeur de fenêtre (px, réactive) */
17
+ width: Ref<number>
18
+ /** hauteur de fenêtre (px, réactive) */
19
+ height: Ref<number>
20
+ /** taille courante : 'xs' | 'sm' | 'md' | 'lg' | 'xl' */
21
+ name: Ref<"xs" | "sm" | "md" | "lg" | "xl">
22
+ /** seuils (réactifs, modifiables via setSizes) */
23
+ sizes: QScreenSizes
24
+ /** less than : width < seuil */
25
+ lt: { sm: Ref<boolean>; md: Ref<boolean>; lg: Ref<boolean>; xl: Ref<boolean> }
26
+ /** greater than : xs > 0 ; sm/md/lg ≥ seuil */
27
+ gt: { xs: Ref<boolean>; sm: Ref<boolean>; md: Ref<boolean>; lg: Ref<boolean> }
28
+ /** taille exacte courante */
29
+ xs: Ref<boolean>
30
+ sm: Ref<boolean>
31
+ md: Ref<boolean>
32
+ lg: Ref<boolean>
33
+ xl: Ref<boolean>
34
+ /** Personnalise les seuils (ex. setSizes({ md: 900 })) */
35
+ setSizes: (all: Partial<QScreenSizes>) => void
36
+ /** Ajoute les classes body .screen--xs … .screen--xl (façon Quasar) */
37
+ setBodyClasses: (condition?: boolean) => void
38
+ }
39
+
40
+ const { width, height } = useWindowSize()
41
+
42
+ const sizes = reactive<QScreenSizes>({ sm: 600, md: 1024, lg: 1440, xl: 1920 })
43
+
44
+ const lt = {
45
+ sm: computed(() => width.value < sizes.sm),
46
+ md: computed(() => width.value < sizes.md),
47
+ lg: computed(() => width.value < sizes.lg),
48
+ xl: computed(() => width.value < sizes.xl),
49
+ }
50
+
51
+ const gt = {
52
+ xs: computed(() => width.value > 0),
53
+ sm: computed(() => width.value >= sizes.sm),
54
+ md: computed(() => width.value >= sizes.md),
55
+ lg: computed(() => width.value >= sizes.lg),
56
+ }
57
+
58
+ const xs = computed(() => width.value < sizes.sm)
59
+ const sm = computed(() => width.value >= sizes.sm && width.value < sizes.md)
60
+ const md = computed(() => width.value >= sizes.md && width.value < sizes.lg)
61
+ const lg = computed(() => width.value >= sizes.lg && width.value < sizes.xl)
62
+ const xl = computed(() => width.value >= sizes.xl)
63
+
64
+ const name = computed<"xs" | "sm" | "md" | "lg" | "xl">(() =>
65
+ xs.value ? "xs" : sm.value ? "sm" : md.value ? "md" : lg.value ? "lg" : "xl",
66
+ )
67
+
68
+ function setSizes(all: Partial<QScreenSizes>) {
69
+ Object.assign(sizes, all)
70
+ }
71
+
72
+ // Classes body façon Quasar (.screen--xs …) — activées via setBodyClasses()
73
+ let bodyClassesEnabled = false
74
+
75
+ const updateBodyClasses = () => {
76
+ if (typeof document === "undefined") return
77
+ const body = document.body
78
+ body.classList.remove("screen--xs", "screen--sm", "screen--md", "screen--lg", "screen--xl")
79
+ body.classList.add(`screen--${name.value}`)
80
+ }
81
+
82
+ watch(name, () => {
83
+ if (bodyClassesEnabled) updateBodyClasses()
84
+ })
85
+
86
+ function setBodyClasses(condition = true) {
87
+ bodyClassesEnabled = condition
88
+ if (condition) updateBodyClasses()
89
+ }
90
+
91
+ export const screen: QScreen = {
92
+ width,
93
+ height,
94
+ name,
95
+ sizes,
96
+ lt,
97
+ gt,
98
+ xs,
99
+ sm,
100
+ md,
101
+ lg,
102
+ xl,
103
+ setSizes,
104
+ setBodyClasses,
105
+ }
package/lib/search.ts ADDED
@@ -0,0 +1,35 @@
1
+ import Fuse from "fuse.js"
2
+
3
+ export interface SearchOptions {
4
+ /** Clés à chercher (chemins "a.b", ou clés simples) */
5
+ keys?: string[]
6
+ /** Seuil de tolérance fuse.js : 0 = exact, 1 = tout matche (défaut 0.4) */
7
+ threshold?: number
8
+ }
9
+
10
+ export interface Searcher<T> {
11
+ search: (query: string) => T[]
12
+ }
13
+
14
+ /**
15
+ * Chercheur flou basé sur Fuse.js, orienté autocomplete.
16
+ * - ignoreLocation : le motif peut matcher n'importe où dans le texte
17
+ * - shouldSort : résultats triés par score de pertinence
18
+ */
19
+ export function createSearcher<T>(items: T[], options: SearchOptions = {}): Searcher<T> {
20
+ const keys = options.keys ?? []
21
+ const fuse = new Fuse<T>(items, {
22
+ keys,
23
+ threshold: options.threshold ?? 0.4,
24
+ ignoreLocation: true,
25
+ shouldSort: true,
26
+ })
27
+
28
+ return {
29
+ search(query) {
30
+ const q = query.trim()
31
+ if (!q || keys.length === 0) return items
32
+ return fuse.search(q).map((r) => r.item)
33
+ },
34
+ }
35
+ }
@@ -0,0 +1,22 @@
1
+ // themeVarsStyle — variables CSS du thème (couleurs + radius) pour les contenus
2
+ // téléportés (dialogs, bottom sheets) qui sortent du div .q-config-provider.
3
+ import { computed } from "vue"
4
+ import type { ComputedRef } from "vue"
5
+ import { contrastText } from "./colors"
6
+ import { isRadiusScale, RADIUS_VALUES } from "./useComponentProps"
7
+ import type { QTheme } from "./config"
8
+
9
+ /** Computed des variables --token / --token-foreground / --q-radius du thème. */
10
+ export function themeVarsStyle(theme: ComputedRef<QTheme>): ComputedRef<Record<string, string>> {
11
+ return computed(() => {
12
+ const style: Record<string, string> = {}
13
+ for (const [token, value] of Object.entries(theme.value.colors ?? {})) {
14
+ if (!value) continue
15
+ style[`--${token}`] = value
16
+ style[`--${token}-foreground`] = contrastText(value)
17
+ }
18
+ const global = theme.value.componentProps?.default?.radius
19
+ if (isRadiusScale(global)) style["--q-radius"] = RADIUS_VALUES[global]
20
+ return style
21
+ })
22
+ }
@@ -0,0 +1,54 @@
1
+ import { computed, inject, toValue } from "vue"
2
+ import type { ComputedRef, MaybeRefOrGetter } from "vue"
3
+ import { qConfigKey } from "./config"
4
+
5
+ /** Échelle de rayon : none (carré) → lg (très arrondi) */
6
+ export type RadiusScale = "none" | "xs" | "sm" | "md" | "lg"
7
+
8
+ /** Prop `radius` : booléen (forme native du composant, ex. pilule) ou échelle */
9
+ export type RadiusProp = boolean | RadiusScale
10
+
11
+ export const RADIUS_VALUES: Record<RadiusScale, string> = {
12
+ none: "0px",
13
+ xs: "2px",
14
+ sm: "4px",
15
+ md: "8px",
16
+ lg: "16px",
17
+ }
18
+
19
+ export const isRadiusScale = (v: unknown): v is RadiusScale =>
20
+ v === "none" || v === "xs" || v === "sm" || v === "md" || v === "lg"
21
+
22
+ /**
23
+ * Props par défaut d'un composant depuis `theme.componentProps` :
24
+ * `default` (appliqué à tous) puis `<Nom>` (spécifique, prioritaire).
25
+ * Déjà fusionnés avec le parent par QConfigProvider — le plus proche gagne.
26
+ */
27
+ export function useComponentProps(name: string): ComputedRef<Record<string, unknown>> {
28
+ const config = inject(qConfigKey, null)
29
+ return computed(() => {
30
+ const all = config?.theme.value.componentProps ?? {}
31
+ const base = all.default ?? {}
32
+ const specific = all[name] ?? {}
33
+ return { ...base, ...specific }
34
+ })
35
+ }
36
+
37
+ /**
38
+ * Prop `radius` effective : prop explicite > composantProps.<Nom> > composantProps.default > undefined.
39
+ * `own` doit être un getter (() => props.radius) pour rester réactif quand la
40
+ * prop change (cas des wrappers comme QInputPassword qui transmettent radius).
41
+ * `undefined` = pas d'arrondi (hérite du `--q-radius` global, 0 par défaut).
42
+ */
43
+ export function useRadius(
44
+ name: string,
45
+ own: MaybeRefOrGetter<RadiusProp | undefined> = undefined,
46
+ ): ComputedRef<RadiusProp | undefined> {
47
+ const defaults = useComponentProps(name)
48
+ return computed(() => toValue(own) ?? (defaults.value.radius as RadiusProp | undefined))
49
+ }
50
+
51
+ /** Style `--q-radius` à poser sur la racine du composant pour une valeur d'échelle. */
52
+ export function radiusStyle(radius: RadiusProp | undefined): Record<string, string> | undefined {
53
+ return isRadiusScale(radius) ? { "--q-radius": RADIUS_VALUES[radius] } : undefined
54
+ }
package/lib/utils.ts ADDED
@@ -0,0 +1,7 @@
1
+ import { type ClassValue, clsx } from "clsx"
2
+ import { twMerge } from "tailwind-merge"
3
+
4
+ /** Fusionne des classes conditionnelles (clsx) et résout les conflits (tailwind-merge). */
5
+ export function cn(...inputs: ClassValue[]) {
6
+ return twMerge(clsx(inputs))
7
+ }
package/module.ts ADDED
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Module Nuxt pour @dnax/ui.
3
+ * Usage : `modules: ["@dnax/ui"]` dans nuxt.config.ts.
4
+ *
5
+ * - Auto-importe les composants Q* (balises kebab-case : <q-btn>, <q-header>…)
6
+ * - Ajoute styles/main.css (safe-area, tokens, styles de tous les composants)
7
+ *
8
+ * Options (clé `dnaxUi`) :
9
+ * components: false — désactive l'auto-import des composants
10
+ * css: false — désactive l'ajout de styles/main.css
11
+ */
12
+ import { addComponentsDir, addPluginTemplate, createResolver, defineNuxtModule } from "@nuxt/kit"
13
+
14
+ export interface DnaxUiModuleOptions {
15
+ /** Auto-importe les composants Q* (défaut : true) */
16
+ components?: boolean
17
+ /** Ajoute styles/main.css (défaut : true) */
18
+ css?: boolean
19
+ /** Installe le handler « retour » : ferme l'overlay ouvert au lieu de naviguer (défaut : true) */
20
+ overlayBack?: boolean
21
+ }
22
+
23
+ /** Custom elements du framework HTML Video.js v10 (@videojs/html) — balises non-Vue */
24
+ const VIDEOJS_CUSTOM_ELEMENTS = [
25
+ "video-player",
26
+ "video-skin",
27
+ "youtube-video",
28
+ "hlsjs-video",
29
+ "mux-video",
30
+ ]
31
+
32
+ export default defineNuxtModule<DnaxUiModuleOptions>({
33
+ meta: {
34
+ name: "@dnax/ui",
35
+ configKey: "dnaxUi",
36
+ compatibility: { nuxt: ">=3.12.0" },
37
+ },
38
+ defaults: {
39
+ components: true,
40
+ css: true,
41
+ overlayBack: true,
42
+ },
43
+ setup(options, nuxt) {
44
+ const { resolve } = createResolver(import.meta.url)
45
+
46
+ // Les balises <video-player>/<youtube-video>… sont des custom elements @videojs/html,
47
+ // pas des composants Vue → les exclure de la résolution (évite le warn
48
+ // « Failed to resolve component »).
49
+ nuxt.options.vue.compilerOptions ??= {}
50
+ const existing = nuxt.options.vue.compilerOptions.isCustomElement
51
+ nuxt.options.vue.compilerOptions.isCustomElement = (tag: string) =>
52
+ VIDEOJS_CUSTOM_ELEMENTS.includes(tag) ||
53
+ (typeof existing === "function" ? existing(tag) : false)
54
+
55
+ if (options.css) {
56
+ const cssPath = resolve("./styles/main.css")
57
+ if (!nuxt.options.css.includes(cssPath)) {
58
+ nuxt.options.css.push(cssPath)
59
+ }
60
+ }
61
+
62
+ if (options.components) {
63
+ addComponentsDir({
64
+ path: resolve("./components"),
65
+ prefix: "", // pas de préfixe : QBtn.vue → <q-btn>
66
+ pathPrefix: false, // pas de préfixe par sous-dossier
67
+ ignore: ["**/internal/**"], // composants internes : non auto-importés
68
+ })
69
+ }
70
+
71
+ // « Retour » navigateur → ferme l'overlay ouvert (dialog, sheet, sidebar…)
72
+ // addPluginTemplate : le template est compilé dans l'app (où #imports existe),
73
+ // évitant une dépendance `nuxt` dans ce package pour les types du plugin.
74
+ if (options.overlayBack) {
75
+ addPluginTemplate({
76
+ filename: "dnax-ui-overlay-back.mjs",
77
+ mode: "client",
78
+ getContents: () => `
79
+ import { defineNuxtPlugin } from "#imports"
80
+ import { installOverlayBackHandler } from "@dnax/ui/runtime"
81
+
82
+ export default defineNuxtPlugin((nuxtApp) => {
83
+ installOverlayBackHandler(nuxtApp.$router)
84
+ })
85
+ `,
86
+ })
87
+ }
88
+ },
89
+ })
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@dnax/ui",
3
+ "version": "0.0.1",
4
+ "module": "index.ts",
5
+ "description": "UI design System",
6
+ "type": "module",
7
+ "exports": {
8
+ ".": "./index.ts",
9
+ "./runtime": "./index.ts",
10
+ "./module": "./module.ts",
11
+ "./styles/main.css": "./styles/main.css"
12
+ },
13
+ "scripts": {
14
+ "generate": "bun scripts/generate-exports.ts"
15
+ },
16
+ "private": false,
17
+ "devDependencies": {
18
+ "@types/bun": "latest"
19
+ },
20
+ "peerDependencies": {
21
+ "typescript": "^7",
22
+ "vue": "^3.5.41"
23
+ },
24
+ "dependencies": {
25
+ "@iconify/vue": "^5.0.1",
26
+ "@nuxt/kit": "^4.5.2",
27
+ "@videojs/html": "^10.0.0-beta.32",
28
+ "@vueuse/core": "^14.4.0",
29
+ "class-variance-authority": "^0.7.1",
30
+ "clsx": "^2.1.1",
31
+ "embla-carousel": "^8.5.0",
32
+ "embla-carousel-vue": "^8.5.0",
33
+ "fuse.js": "^7.1.0",
34
+ "shiki": "^3.0.0",
35
+ "swiper": "^14.2.0",
36
+ "tailwind-merge": "^3.6.0",
37
+ "tw-animate-css": "^1.4.0",
38
+ "vue-router": "^5.3.0",
39
+ "vue-sonner": "^2.0.9"
40
+ }
41
+ }
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Regenerates `index.ts` with one explicit export per component.
3
+ * Run with: bun scripts/generate-exports.ts
4
+ */
5
+ import { readdir } from "node:fs/promises"
6
+
7
+ const componentsDir = new URL("../components/", import.meta.url)
8
+ const indexFile = new URL("../index.ts", import.meta.url)
9
+
10
+ const toPascalCase = (name: string) =>
11
+ name
12
+ .split(/[-_]/)
13
+ .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
14
+ .join("")
15
+
16
+ const files = (await readdir(componentsDir))
17
+ .filter((file) => file.endsWith(".vue"))
18
+ .sort()
19
+
20
+ const exports = files
21
+ .map(
22
+ (file) =>
23
+ `export { default as ${toPascalCase(file.replace(/\.vue$/, ""))} } from "./components/${file}"`,
24
+ )
25
+ .join("\n")
26
+
27
+ // Exports manuels (composables / helpers) — conservés à chaque régénération
28
+ const manualExports = `
29
+ export { $q, usePlugin, useQ, QPlugin, dialogStack, closeDialog, bottomSheetStack, closeBottomSheet } from "./lib/q"
30
+ export type {
31
+ DialogOptions,
32
+ DialogController,
33
+ BottomSheetOptions,
34
+ BottomSheetController,
35
+ NotifyOptions,
36
+ NotifyController,
37
+ } from "./lib/q"
38
+ export { platform, qBreakpoints } from "./lib/platform"
39
+ export type { QPlatform } from "./lib/platform"
40
+ export { screen } from "./lib/screen"
41
+ export type { QScreen } from "./lib/screen"
42
+ export { loading } from "./lib/loading"
43
+ export type { LoadingOptions, QLoadingPlugin } from "./lib/loading"
44
+ export {
45
+ registerOverlay,
46
+ unregisterOverlay,
47
+ hasOpenOverlays,
48
+ closeTopmostOverlay,
49
+ useOverlayBack,
50
+ installOverlayBackHandler,
51
+ } from "./lib/overlayBack"
52
+ export type { OverlayHandle } from "./lib/overlayBack"
53
+ `
54
+
55
+ await Bun.write(indexFile, `${exports}\n${manualExports}`)
56
+ console.log(`✓ ${files.length} composant(s) exporté(s) dans index.ts`)