@dnax/ui 0.0.53 → 0.0.54

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.
@@ -13,7 +13,7 @@ import QBottomSheetProvider from "./QBottomSheetProvider.vue"
13
13
  import QImagePreviewProvider from "./QImagePreviewProvider.vue"
14
14
  import { qConfigKey, qProvidersKey } from "../lib/config"
15
15
  import type { QAppLang, QConfigContext, QTheme, ThemeMode } from "../lib/config"
16
- import { isRadiusScale, RADIUS_VALUES } from "../lib/useComponentProps"
16
+ import { themeVars } from "../lib/themeVars"
17
17
 
18
18
  interface Props {
19
19
  /**
@@ -119,40 +119,51 @@ watch(
119
119
 
120
120
  provide<QConfigContext>(qConfigKey, { theme: mergedTheme, isDark, lang })
121
121
 
122
+ // — Dev : `componentProps` n'est PAS un spread de props — une clé non lue est ignorée
123
+ // silencieusement (cf. `.memory/knowledges.md`). On le signale, une fois par clé.
124
+ /** Clés de `componentProps.<Nom>` réellement lues, en plus du `radius` générique. */
125
+ const EXTRA_COMPONENT_PROP_KEYS: Record<string, readonly string[]> = { QMap: ["apiKey"] }
126
+
127
+ const IS_DEV = !!((import.meta as any).env?.DEV ?? (import.meta as any).dev)
128
+ if (IS_DEV) {
129
+ const warned = new Set<string>()
130
+ watch(
131
+ mergedTheme,
132
+ (theme) => {
133
+ if (typeof document === "undefined") return // pas de bruit au SSR/build
134
+ for (const [name, entry] of Object.entries(theme.componentProps ?? {})) {
135
+ if (!entry || typeof entry !== "object") continue
136
+ const allowed = new Set<string>(["radius", ...(EXTRA_COMPONENT_PROP_KEYS[name] ?? [])])
137
+ const ignored = Object.keys(entry).filter((key) => !allowed.has(key))
138
+ if (!ignored.length) continue
139
+ const signature = `${name}:${ignored.join(",")}`
140
+ if (warned.has(signature)) continue
141
+ warned.add(signature)
142
+ console.warn(
143
+ `[dnax/ui] componentProps.${name} : clé(s) ignorée(s) → ${ignored.map((k) => `"${k}"`).join(", ")}.\n` +
144
+ ` Ces clés sont lues pour ${name} : ${[...allowed].map((k) => `"${k}"`).join(", ")}.\n` +
145
+ ` Pour une couleur/un fond, passez par theme.vars (ex. { "--q-field-bg": "#eef7ee" }) ou du CSS.`,
146
+ )
147
+ }
148
+ },
149
+ { immediate: true, deep: true },
150
+ )
151
+ }
152
+
122
153
  // Providers intégrés ($q.dialog + $q.notify) : rendus UNE fois par le
123
154
  // QConfigProvider le plus externe (les imbriqués ne re-rendent pas → pas de doublons)
124
155
  const hasProviders = inject(qProvidersKey, false)
125
156
  provide(qProvidersKey, true)
126
157
  const isProvidersRoot = computed(() => !hasProviders)
127
158
 
128
- /** Calcule un foreground lisible (blanc ou sombre) pour un fond donné. */
129
- const textColorFor = (bg: string): string => {
130
- let hex = bg.trim().replace(/^#/, "")
131
- if (hex.length === 3) hex = hex.split("").map((c) => c + c).join("")
132
- if (!/^[0-9a-f]{6}$/i.test(hex)) return "#ffffff"
133
- const r = parseInt(hex.slice(0, 2), 16)
134
- const g = parseInt(hex.slice(2, 4), 16)
135
- const b = parseInt(hex.slice(4, 6), 16)
136
- const luminance = 0.299 * r + 0.587 * g + 0.114 * b
137
- return luminance > 150 ? "#1d1d1d" : "#ffffff"
138
- }
139
-
140
- const themeStyle = computed<Record<string, string>>(() => {
141
- const style: Record<string, string> = {
142
- "color-scheme": isDark.value ? "dark" : "light",
143
- }
144
- for (const [token, value] of Object.entries(mergedTheme.value.colors ?? {})) {
145
- if (!value) continue
146
- style[`--${token}`] = value
147
- style[`--${token}-foreground`] = textColorFor(value)
148
- }
149
- // Arrondi global : composantProps.default.radius → --q-radius hérité par TOUS
150
- // les composants dont le CSS utilise var(--q-radius). Une prop/override
151
- // spécifique (useRadius) pose son propre --q-radius inline et prime.
152
- const global = mergedTheme.value.componentProps?.default?.radius
153
- if (isRadiusScale(global)) style["--q-radius"] = RADIUS_VALUES[global]
154
- return style
155
- })
159
+ /**
160
+ * Variables CSS du thème : `color-scheme` + couleurs, `--q-radius` et `vars` libres
161
+ * (logique partagée avec les providers d'overlays téléportés, cf. `themeVars`).
162
+ */
163
+ const themeStyle = computed<Record<string, string>>(() => ({
164
+ "color-scheme": isDark.value ? "dark" : "light",
165
+ ...themeVars(mergedTheme.value),
166
+ }))
156
167
 
157
168
  // Thème GLOBAL (provider racine) : pose aussi les variables sur <html> — les
158
169
  // overlays téléportés au body (dialogs $q.dialog, …) perdent l'héritage du div
@@ -60,6 +60,7 @@ import { radiusStyle, radiusValue, useRadius } from "../lib/useComponentProps"
60
60
  import type { RadiusProp } from "../lib/useComponentProps"
61
61
  import { useOverlayBack } from "../lib/overlayBack"
62
62
  import { createSearcher } from "../lib/search"
63
+ import { optionsOf, selectionOf } from "../lib/select"
63
64
 
64
65
  interface Props {
65
66
  /** Valeur sélectionnée : option, valeur (emit-value), ou tableau (multiple) */
@@ -337,7 +338,9 @@ watch([popupPlacement, popupOffset], onPopupViewportChange)
337
338
  const isObjectOption = (o: any): boolean => o !== null && typeof o === "object"
338
339
 
339
340
  const normalizedOptions = computed<any[]>(() =>
340
- props.options.map((o) => {
341
+ // `options` peut arriver `false` (v-if/&&, config non chargée) : sans garde,
342
+ // `props.options.map` jetait et l'option par défaut ne s'affichait plus.
343
+ optionsOf(props.options).map((o) => {
341
344
  if (isObjectOption(o)) return o
342
345
  // Objet normalisé : garde la valeur d'origine (non-énumérable → invisible
343
346
  // pour l'affichage, la recherche fuse et les clés de rendu)
@@ -361,8 +364,9 @@ const getOptionLabel = (opt: any): string => {
361
364
 
362
365
  // — Sélection (dérivée du modèle) —
363
366
  const selectedOptions = computed<any[]>(() => {
364
- if (props.modelValue === undefined || props.modelValue === null) return []
365
- const list = props.multiple ? (props.modelValue as any[]) : [props.modelValue]
367
+ // En `multiple`, un modèle scalaire (ex. `false`) ne doit PAS faire jeter `list.map`
368
+ // (tout le popup cessait de se rendre) : `selectionOf` le traite comme vide.
369
+ const list = selectionOf(props.modelValue, props.multiple)
366
370
  return list
367
371
  .map((v) => {
368
372
  if (props.emitValue) return normalizedOptions.value.find((o) => getOptionValue(o) === v)
@@ -417,7 +421,9 @@ const clear = () => {
417
421
  }
418
422
 
419
423
  const hasValue = computed(() =>
420
- props.multiple ? (props.modelValue as any[])?.length > 0 : props.modelValue !== undefined && props.modelValue !== null,
424
+ props.multiple
425
+ ? Array.isArray(props.modelValue) && props.modelValue.length > 0
426
+ : props.modelValue !== undefined && props.modelValue !== null,
421
427
  )
422
428
 
423
429
  // — Recherche floue (fuse.js) —
package/lib/config.ts CHANGED
@@ -24,6 +24,14 @@ export interface QTheme {
24
24
  lang?: QAppLang
25
25
  /** Props par défaut par composant (clé = nom du composant, ex. "QBtn") */
26
26
  componentProps?: Record<string, Record<string, unknown>>
27
+ /**
28
+ * Variables CSS libres posées avec le thème, puis héritées par tout le sous-arbre
29
+ * (overlays téléportés compris : dialogs, bottom sheets). Clé avec ou sans `--`
30
+ * (ex. `{ "q-field-bg": "#eef7ee" }`). Appliquées **en dernier**, elles peuvent donc
31
+ * surcharger les tokens de `colors`. Utile pour ce qu'aucun token ne couvre
32
+ * (ex. le fond des champs, cf. `--q-field-bg`).
33
+ */
34
+ vars?: Record<string, string>
27
35
  }
28
36
 
29
37
  export interface QConfigContext {
package/lib/qrcode.ts CHANGED
@@ -1,11 +1,13 @@
1
- // qrcode — encodage QR et tracé SVG. Le moteur d'encodage est la dépendance `qrcode`
2
- // (déjà présente, sans types — cf. `shims.d.ts`) : elle fournit la matrice de modules, ce
3
- // module la traduit en **chemin SVG** (une suite de rectangles horizontaux, bien plus
4
- // compact qu'un carré par module) — d'où un rendu net à toutes les tailles, thémable et
5
- // rendable en SSR.
1
+ // qrcode — encodage QR et tracé SVG. Le moteur d'encodage est `uqr` (ESM pur, sans
2
+ // dépendance) : il fournit la matrice de modules, ce module la traduit en **chemin SVG**
3
+ // (une suite de rectangles horizontaux, bien plus compact qu'un carré par module) — d'où
4
+ // un rendu net à toutes les tailles, thémable et rendable en SSR.
5
+ //
6
+ // `uqr` est **ESM** (contrairement à l'ancien `qrcode`, CJS sans `default`, qui cassait le
7
+ // bundle client — cf. `.memory/warnings.md`) : aucun interop esbuild, aucun `optimizeDeps`.
6
8
  //
7
9
  // Pur (aucun DOM) → testable hors navigateur, comme `lib/pagePadding.ts`.
8
- import QRCode from "qrcode"
10
+ import { encode } from "uqr"
9
11
 
10
12
  /** Niveau de correction d'erreur : L (~7 %) → H (~30 %) */
11
13
  export type QrEcc = "L" | "M" | "Q" | "H"
@@ -39,10 +41,25 @@ export function encodeQr(value: string, options: QrEncodeOptions = {}): QrMatrix
39
41
  if (typeof value !== "string" || value === "") return undefined
40
42
 
41
43
  try {
42
- const settings: Record<string, unknown> = { errorCorrectionLevel: options.ecc ?? "M" }
43
- if (typeof options.version === "number") settings.version = options.version
44
+ const { size, data } = encode(value, {
45
+ ecc: options.ecc ?? "M",
46
+ // La zone de silence est gérée par dnax.ui (`margin`) : on demande la matrice nue.
47
+ border: 0,
48
+ // `uqr` raisonne en bornes de version, pas en version forcée.
49
+ ...(typeof options.version === "number"
50
+ ? { minVersion: options.version, maxVersion: options.version }
51
+ : {}),
52
+ })
53
+
54
+ // `uqr` renvoie un `boolean[][]` ; on l'aplatit au contrat `data[y * size + x]`
55
+ // (1 = module sombre) attendu par `isDark` et le tracé.
56
+ const flat = new Uint8Array(size * size)
57
+ let i = 0
58
+ for (const row of data) {
59
+ for (let x = 0; x < size; x++) flat[i++] = row[x] ? 1 : 0
60
+ }
44
61
 
45
- return QRCode.create(value, settings).modules
62
+ return { size, data: flat }
46
63
  } catch {
47
64
  return undefined
48
65
  }
@@ -0,0 +1,42 @@
1
+ // Tests unitaires des coercitions défensives de QSelect (`optionsOf` / `selectionOf`).
2
+ // Régression : `multiple` + modèle scalaire (`false`) faisait jeter `list.map` et
3
+ // empêchait le popup entier de se rendre.
4
+ import { describe, expect, it } from "bun:test"
5
+ import { optionsOf, selectionOf } from "./select"
6
+
7
+ describe("optionsOf", () => {
8
+ it("laisse passer un tableau (même vide)", () => {
9
+ expect(optionsOf(["a", "b"])).toEqual(["a", "b"])
10
+ expect(optionsOf([])).toEqual([])
11
+ })
12
+
13
+ it("traite toute valeur non-tableau comme vide (au lieu de planter)", () => {
14
+ expect(optionsOf(false)).toEqual([])
15
+ expect(optionsOf(undefined)).toEqual([])
16
+ expect(optionsOf(null)).toEqual([])
17
+ expect(optionsOf("oops")).toEqual([])
18
+ expect(optionsOf({ value: 1 })).toEqual([])
19
+ })
20
+ })
21
+
22
+ describe("selectionOf", () => {
23
+ it("multiple : accepte un tableau", () => {
24
+ expect(selectionOf(["a", "b"], true)).toEqual(["a", "b"])
25
+ expect(selectionOf([], true)).toEqual([])
26
+ })
27
+
28
+ it("multiple + scalaire → rien de sélectionné (le bug d'origine : `false`)", () => {
29
+ expect(selectionOf(false, true)).toEqual([])
30
+ expect(selectionOf(true, true)).toEqual([])
31
+ expect(selectionOf("a", true)).toEqual([])
32
+ expect(selectionOf(0, true)).toEqual([])
33
+ })
34
+
35
+ it("simple : emballe la valeur, sauf null/undefined", () => {
36
+ expect(selectionOf("a", false)).toEqual(["a"])
37
+ expect(selectionOf(0, false)).toEqual([0])
38
+ expect(selectionOf(false, false)).toEqual([false])
39
+ expect(selectionOf(null, false)).toEqual([])
40
+ expect(selectionOf(undefined, false)).toEqual([])
41
+ })
42
+ })
package/lib/select.ts ADDED
@@ -0,0 +1,29 @@
1
+ // Coercitions défensives de QSelect (`lib/select.ts`).
2
+ //
3
+ // Un `v-model` ou des `options` mal formés ne doivent **jamais** casser le rendu : un
4
+ // `list.map is not a function` dans un `computed` du template tue tout le popup, sans
5
+ // message utile. Cas réel : `<q-select multiple v-model="x">` avec `x` initialisé à
6
+ // `false` (défaut d'un `ref` booléen, retour d'un `&&`, config non chargée) — le garde
7
+ // `=== null`/`=== undefined` ne l'attrape pas, puis `false.map(...)` jette.
8
+ //
9
+ // Pur (aucun DOM) → testable hors navigateur, comme `lib/pagePadding.ts`.
10
+
11
+ /**
12
+ * Tableau des options. `options` doit être un tableau : toute autre valeur (ex. `false`,
13
+ * `0`, un objet isolé) est traitée comme **vide** plutôt que de faire planter
14
+ * `normalizedOptions`.
15
+ */
16
+ export const optionsOf = (value: unknown): any[] => (Array.isArray(value) ? value : [])
17
+
18
+ /**
19
+ * Tableau de sélection dérivé du `v-model` :
20
+ * - `null` / `undefined` → rien ;
21
+ * - `multiple` → le modèle **doit** être un tableau ; une valeur scalaire (ex. `false`)
22
+ * vaut « rien de sélectionné » (et non une sélection de cette valeur) ;
23
+ * - mode simple → on emballe la valeur.
24
+ */
25
+ export const selectionOf = (value: unknown, multiple: boolean): any[] => {
26
+ if (value === undefined || value === null) return []
27
+ if (multiple) return Array.isArray(value) ? value : []
28
+ return [value]
29
+ }
@@ -0,0 +1,42 @@
1
+ // Tests unitaires des variables CSS du thème (`themeVars`) — partagées par
2
+ // QConfigProvider et les providers d'overlays téléportés.
3
+ import { describe, expect, it } from "bun:test"
4
+ import { themeVars } from "./themeVars"
5
+
6
+ describe("themeVars", () => {
7
+ it("expose les couleurs en `--token` + `--token-foreground`", () => {
8
+ const style = themeVars({ colors: { primary: "#1976d2" } })
9
+ expect(style["--primary"]).toBe("#1976d2")
10
+ // foreground lisible calculé (clair sur fond sombre)
11
+ expect(style["--primary-foreground"]).toBe("#ffffff")
12
+ })
13
+
14
+ it("traduit `componentProps.default.radius` en `--q-radius`", () => {
15
+ expect(themeVars({ componentProps: { default: { radius: "md" } } })["--q-radius"]).toBe("8px")
16
+ })
17
+
18
+ it("ignore un `default.radius` non-échelle (`true` = forme native)", () => {
19
+ expect(themeVars({ componentProps: { default: { radius: true } } })["--q-radius"]).toBeUndefined()
20
+ })
21
+
22
+ it("pose les `vars` libres, avec ou sans préfixe `--`", () => {
23
+ const style = themeVars({ vars: { "--q-field-bg": "#eef7ee", "q-field-bg-filled": "red" } })
24
+ expect(style["--q-field-bg"]).toBe("#eef7ee")
25
+ expect(style["--q-field-bg-filled"]).toBe("red")
26
+ })
27
+
28
+ it("donne le dernier mot aux `vars` sur les tokens de `colors`", () => {
29
+ const style = themeVars({ colors: { primary: "#111" }, vars: { "--primary": "#222" } })
30
+ expect(style["--primary"]).toBe("#222")
31
+ })
32
+
33
+ it("ignore les valeurs vides", () => {
34
+ const style = themeVars({ colors: { primary: "" }, vars: { "--x": "" } })
35
+ expect(style["--primary"]).toBeUndefined()
36
+ expect(style["--x"]).toBeUndefined()
37
+ })
38
+
39
+ it("renvoie un objet vide pour un thème vide", () => {
40
+ expect(themeVars({})).toEqual({})
41
+ })
42
+ })
package/lib/themeVars.ts CHANGED
@@ -1,22 +1,40 @@
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.
1
+ // themeVars — variables CSS du thème (couleurs, radius global, `vars` libres) pour les
2
+ // contenus téléportés (dialogs, bottom sheets) qui sortent du div .q-config-provider.
3
3
  import { computed } from "vue"
4
4
  import type { ComputedRef } from "vue"
5
5
  import { contrastText } from "./colors"
6
6
  import { isRadiusScale, RADIUS_VALUES } from "./useComponentProps"
7
7
  import type { QTheme } from "./config"
8
8
 
9
- /** Computed des variables --token / --token-foreground / --q-radius du thème. */
9
+ /**
10
+ * Variables CSS d'un thème : `--<token>` (+ `--<token>-foreground` calculé) pour
11
+ * `colors`, `--q-radius` pour `componentProps.default.radius`, puis les `vars` libres
12
+ * **en dernier** (elles ont le dernier mot). Pur → testable hors navigateur.
13
+ */
14
+ export function themeVars(theme: QTheme): Record<string, string> {
15
+ const style: Record<string, string> = {}
16
+
17
+ for (const [token, value] of Object.entries(theme.colors ?? {})) {
18
+ if (!value) continue
19
+ style[`--${token}`] = value
20
+ style[`--${token}-foreground`] = contrastText(value)
21
+ }
22
+
23
+ // Arrondi global : hérité par TOUS les composants dont le CSS lit var(--q-radius).
24
+ // Une prop/override spécifique (`useRadius`) pose son propre --q-radius inline et prime.
25
+ const global = theme.componentProps?.default?.radius
26
+ if (isRadiusScale(global)) style["--q-radius"] = RADIUS_VALUES[global]
27
+
28
+ // Échappatoire : toute variable CSS, avec ou sans préfixe `--`.
29
+ for (const [name, value] of Object.entries(theme.vars ?? {})) {
30
+ if (!value) continue
31
+ style[name.startsWith("--") ? name : `--${name}`] = value
32
+ }
33
+
34
+ return style
35
+ }
36
+
37
+ /** Computed des variables du thème (cf. `themeVars`). */
10
38
  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
- })
39
+ return computed(() => themeVars(theme.value))
22
40
  }
package/module.ts CHANGED
@@ -50,6 +50,17 @@ export default defineNuxtModule<DnaxUiModuleOptions>({
50
50
  })
51
51
  }
52
52
 
53
+ // Dépendances CJS sans export ESM `default` explicite : esbuild doit les pré-bundler
54
+ // (interop) sinon, servies brutes, le navigateur échoue sur « does not provide an
55
+ // export named 'default' ». `leaflet` (lib/mapLeaflet.ts) est dans ce cas ; le code
56
+ // tolère déjà les deux formes (`default ?? module`), ceci est une ceinture-bretelles
57
+ // côté hôte. Ne pas pointer une sous-entrée sur `index.ts` (cf. warnings en mémoire).
58
+ nuxt.hook("vite:extendConfig", (config) => {
59
+ const optimizeDeps = ((config as any).optimizeDeps ||= {})
60
+ const include: string[] = (optimizeDeps.include ||= [])
61
+ if (!include.includes("leaflet")) include.push("leaflet")
62
+ })
63
+
53
64
  // « Retour » navigateur → ferme l'overlay ouvert (dialog, sheet, sidebar…)
54
65
  // addPluginTemplate : le template est compilé dans l'app (où #imports existe),
55
66
  // évitant une dépendance `nuxt` dans ce package pour les types du plugin.
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "@dnax/ui",
3
- "version": "0.0.53",
3
+ "version": "0.0.54",
4
4
  "module": "index.ts",
5
5
  "description": "UI design System",
6
6
  "type": "module",
7
7
  "exports": {
8
8
  ".": "./index.ts",
9
- "./runtime": "./index.ts",
9
+ "./registry": "./index.ts",
10
+ "./runtime": "./runtime.ts",
10
11
  "./module": "./module.ts",
11
12
  "./styles/main.css": "./styles/main.css"
12
13
  },
@@ -36,11 +37,11 @@
36
37
  "leaflet": "^1.9.4",
37
38
  "maplibre-gl": "^5.24.0",
38
39
  "moment": "^2.30.1",
39
- "qrcode": "^1.5.4",
40
40
  "shiki": "^3.0.0",
41
41
  "swiper": "^14.2.0",
42
42
  "tailwind-merge": "^3.6.0",
43
43
  "tw-animate-css": "^1.4.0",
44
+ "uqr": "^0.1.3",
44
45
  "uuid": "^14.0.2",
45
46
  "vue-router": "^5.3.0",
46
47
  "vue-sonner": "^2.0.9"
package/runtime.ts ADDED
@@ -0,0 +1,48 @@
1
+ // runtime.ts — **surface runtime** consommée par les plugins (module Nuxt + plugins de
2
+ // l'app hôte). Elle contient, et rien de plus :
3
+ // - l'API directive / overlay (`installOverlayBackHandler`, `v-close`, `v-ripple`,
4
+ // `v-touch-*`, `v-intersection`) ;
5
+ // - l'API impérative `$q` (composables `usePlugin` / `useDialogPluginComponent`…) ;
6
+ // - les **providers** de `$q` (dialog, bottom sheet, notify, loading, image preview),
7
+ // montés au boot par l'app hôte.
8
+ //
9
+ // ⚠️ Ne JAMAIS réexporter `index.ts` ici. Le barrel importe **tous** les composants et,
10
+ // avec eux, leurs dépendances lourdes (echarts, @maptiler/sdk, swiper, shiki, uqr…) : une
11
+ // sous-entrée qui pointe sur `index.ts` les évalue au boot, même si l'app n'utilise aucun
12
+ // de ces composants. C'est tout l'intérêt de ce fichier — voir `.memory/warnings.md`.
13
+ //
14
+ // Pour *résoudre un composant par son nom* (tableaux d'API, docs), importer le barrel
15
+ // `@dnax/ui` — pas cette entrée.
16
+ export {
17
+ installOverlayBackHandler,
18
+ registerOverlay,
19
+ unregisterOverlay,
20
+ hasOpenOverlays,
21
+ closeTopmostOverlay,
22
+ useOverlayBack,
23
+ } from "./lib/overlayBack"
24
+ export type { OverlayHandle } from "./lib/overlayBack"
25
+ export { markOverlayClose, closeParentOverlay, vClose } from "./lib/closeOverlay"
26
+ export { vRipple } from "./lib/ripple"
27
+ export { vTouchPan } from "./lib/touchPan"
28
+ export { vTouchHold } from "./lib/touchHold"
29
+ export { vTouchSwipe } from "./lib/touchSwipe"
30
+ export { vTouchRepeat } from "./lib/touchRepeat"
31
+ export { vIntersection } from "./lib/intersection"
32
+
33
+ // — API impérative `$q` (composables) —
34
+ export {
35
+ $q,
36
+ usePlugin,
37
+ useQ,
38
+ QPlugin,
39
+ useDialogPluginComponent,
40
+ useBottomSheetPluginComponent,
41
+ } from "./lib/q"
42
+
43
+ // — Providers de `$q` (montés au boot par l'app hôte) —
44
+ export { default as QDialogProvider } from "./components/QDialogProvider.vue"
45
+ export { default as QBottomSheetProvider } from "./components/QBottomSheetProvider.vue"
46
+ export { default as QNotifyProvider } from "./components/QNotifyProvider.vue"
47
+ export { default as QLoadingProvider } from "./components/QLoadingProvider.vue"
48
+ export { default as QImagePreviewProvider } from "./components/QImagePreviewProvider.vue"
package/shims.d.ts CHANGED
@@ -7,19 +7,13 @@ declare module "*.css?raw" {
7
7
 
8
8
  // Leaflet ne publie pas de types (ils vivent dans `@types/leaflet`, un paquet séparé) :
9
9
  // le moteur `lib/mapLeaflet.ts` le manipule donc en `any`, comme le SDK MapTiler.
10
+ //
11
+ // ⚠️ Ce `default` est une commodité de **typage** : au runtime `leaflet` est un UMD, sans
12
+ // export ESM `default` garanti (selon la pré-bundlisation Vite). C'est pourquoi
13
+ // `lib/mapLeaflet.ts` importe en **dynamique** puis prend `leaflet.default ?? leaflet` —
14
+ // jamais un `import L from "leaflet"` statique (même piège que l'ancien `qrcode`, cf.
15
+ // `.memory/warnings.md`).
10
16
  declare module "leaflet" {
11
17
  const leaflet: any
12
18
  export default leaflet
13
19
  }
14
-
15
- // Même situation pour `qrcode` (=`@types/qrcode` séparé) : seul `create()` est utilisé
16
- // (matrice de modules) — le rendu est fait par dnax.ui (`lib/qrcode.ts`).
17
- declare module "qrcode" {
18
- const qrcode: {
19
- create: (
20
- text: string,
21
- options?: { errorCorrectionLevel?: "L" | "M" | "Q" | "H"; version?: number },
22
- ) => { modules: { size: number; data: ArrayLike<number> } }
23
- }
24
- export default qrcode
25
- }
package/styles/main.css CHANGED
@@ -400,7 +400,7 @@ html.dark body {
400
400
  }
401
401
 
402
402
  .dark .q-field__control {
403
- background-color: var(--muted);
403
+ background-color: var(--q-field-bg, var(--muted));
404
404
  border-color: var(--border);
405
405
  color: var(--foreground);
406
406
  }
@@ -2021,7 +2021,11 @@ html.dark body {
2021
2021
  gap: 6px;
2022
2022
  min-height: 40px;
2023
2023
  padding: 0 12px;
2024
- background-color: #fff;
2024
+ /* Fond du champ, partagé par TOUS les champs (QInput, QSelect, QAutocomplete,
2025
+ QDatePicker, QCountryPicker, QInputTag, QInputOtp…). Surchargeable globalement via
2026
+ `--q-field-bg` (héritée par le sous-arbre) — `theme.vars: { "--q-field-bg": "…" }` —
2027
+ ou localement par CSS. La variante `filled` lit `--q-field-bg-filled`. */
2028
+ background-color: var(--q-field-bg, #fff);
2025
2029
  border: 1px solid rgb(0 0 0 / 0.22);
2026
2030
  border-radius: var(--q-radius);
2027
2031
  transition:
@@ -2031,18 +2035,18 @@ html.dark body {
2031
2035
  }
2032
2036
 
2033
2037
  .q-field--outlined .q-field__control {
2034
- background-color: transparent;
2038
+ background-color: var(--q-field-bg, transparent);
2035
2039
  }
2036
2040
 
2037
2041
  .q-field--filled .q-field__control {
2038
- background-color: rgb(0 0 0 / 0.05);
2042
+ background-color: var(--q-field-bg-filled, rgb(0 0 0 / 0.05));
2039
2043
  border-color: transparent;
2040
2044
  border-bottom: 1px solid rgb(0 0 0 / 0.3);
2041
2045
  border-radius: var(--q-radius) var(--q-radius) 0 0;
2042
2046
  }
2043
2047
 
2044
2048
  .q-field--borderless .q-field__control {
2045
- background-color: transparent;
2049
+ background-color: var(--q-field-bg, transparent);
2046
2050
  border-color: transparent;
2047
2051
  }
2048
2052