@dnax/ui 0.0.46 → 0.0.49

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 (47) hide show
  1. package/components/QBottomSheet.vue +172 -6
  2. package/components/QBtn.vue +3 -4
  3. package/components/QContainer.vue +2 -2
  4. package/components/QImagePicker.vue +28 -1
  5. package/components/QInputChat.vue +859 -0
  6. package/components/QInputCurrency.vue +319 -0
  7. package/components/QItemLabel.vue +99 -0
  8. package/components/QMap.vue +410 -0
  9. package/components/QNumericKeyboard.vue +304 -0
  10. package/components/QPage.vue +26 -6
  11. package/components/QPageContainer.vue +32 -0
  12. package/components/QPopupProxy.vue +378 -0
  13. package/components/QQrcode.vue +132 -0
  14. package/components/QSpreadsheet.vue +395 -69
  15. package/components/QTime.vue +845 -0
  16. package/components/QTimeline.vue +111 -0
  17. package/components/QTimelineEntry.vue +271 -0
  18. package/index.ts +11 -1
  19. package/lib/bottomSheet.test.ts +121 -0
  20. package/lib/bottomSheet.ts +115 -0
  21. package/lib/color.ts +13 -0
  22. package/lib/currency.test.ts +193 -0
  23. package/lib/currency.ts +301 -0
  24. package/lib/fixedLayout.ts +12 -11
  25. package/lib/icons.ts +3 -20
  26. package/lib/map.test.ts +365 -0
  27. package/lib/map.ts +438 -0
  28. package/lib/mapEngine.ts +158 -0
  29. package/lib/mapLeaflet.ts +142 -0
  30. package/lib/mapMaplibre.ts +106 -0
  31. package/lib/mapMaptiler.ts +93 -0
  32. package/lib/numericKeyboard.test.ts +179 -0
  33. package/lib/numericKeyboard.ts +166 -0
  34. package/lib/pagePadding.test.ts +37 -0
  35. package/lib/pagePadding.ts +32 -0
  36. package/lib/qrcode.test.ts +103 -0
  37. package/lib/qrcode.ts +125 -0
  38. package/lib/spreadsheetChanges.test.ts +230 -0
  39. package/lib/spreadsheetChanges.ts +331 -0
  40. package/package.json +5 -17
  41. package/shims.d.ts +19 -0
  42. package/styles/main.css +702 -893
  43. package/components/QTiptap.vue +0 -996
  44. package/components/internal/QTiptapTableView.vue +0 -123
  45. package/components/internal/QTiptapTaskItemView.vue +0 -49
  46. package/lib/tiptap-table.ts +0 -19
  47. package/lib/tiptap-task-list.ts +0 -16
package/styles/main.css CHANGED
@@ -1,3 +1,16 @@
1
+ /* Feuille de style du SDK MapTiler (nécessaire au canvas, aux contrôles et aux bulles
2
+ de `q-map`) — inlinée par Vite au build, aucun chargement à l'exécution. */
3
+ @import "@maptiler/sdk/style.css";
4
+
5
+ /* Feuille de style de Leaflet (provider `leaflet` : tuiles, contrôles, bulles,
6
+ épingles) — inlinée par Vite, qui résout aussi les `url()` des images de marqueur. */
7
+ @import "leaflet/dist/leaflet.css";
8
+
9
+ /* Feuille de style de MapLibre (provider `maplibre`) : elle est déjà incluse dans
10
+ `@maptiler/sdk/style.css` (le SDK concatène `maplibre-gl/dist/maplibre-gl.css` à la
11
+ sienne — mêmes classes `.maplibregl-*`, même version de MapLibre) : pas de second
12
+ import, sinon les règles seraient dupliquées. */
13
+
1
14
  /* ===== Reset global (Preflight Tailwind) =====
2
15
  box-sizing: border-box partout : width:100% + padding (q-container, q-btn
3
16
  stretch, q-toolbar…) ne débordent plus du viewport (scroll horizontal).
@@ -42,7 +55,7 @@ body {
42
55
  3500 popup inline de QSelect (téléporté dans <body> : il doit passer
43
56
  au-dessus d'un overlay quand le champ est dans un dialog/sheet)
44
57
  → --q-z-popup
45
- 4000 palettes tiptap · 7000 loading global · 9999 tooltip ·
58
+ 7000 loading global · 9999 tooltip ·
46
59
  10000 image preview
47
60
  Surchargez --q-z-menu ou --q-z-popup si votre application a un overlay plus haut. */
48
61
  :root {
@@ -101,27 +114,58 @@ body {
101
114
  /* Barre d'action basse */
102
115
  /* Safe-area intégrées à la règle de base .q-footer (plus bas) */
103
116
 
104
- /* Contenu de page : pas de padding par défaut (barres dans le flux).
105
- Compensation UNIQUEMENT si une barre est fixée (:has) : 50px + safe-area.
106
- Safe-area bas : le contenu scroillé jusqu'au bout ne passe pas sous la barre
107
- d'accueil iOS. */
117
+ /* Contenu de page : padding utilisateur (prop `padding` → `--q-page-padding`),
118
+ compensation des barres `fixed` (variables posées par `lib/fixedLayout.ts`) et
119
+ safe-area basse. Les deux sources de padding sont **composées ici** (jamais écrasées
120
+ l'une par l'autre) ; `max()` évite de compter deux fois la safe-area quand la barre
121
+ basse fixe est déjà mesurée (sa hauteur la contient). */
108
122
  .q-page {
109
123
  flex: 1 1 auto; /* prend l'espace restant */
110
- padding-bottom: 0;
111
- padding-bottom: constant(safe-area-inset-bottom);
112
- padding-bottom: env(safe-area-inset-bottom);
124
+ padding: var(--q-page-padding, 0px);
125
+ padding-top: calc(var(--q-page-offset, 0px) + var(--q-page-padding, 0px));
126
+ padding-bottom: calc(var(--q-page-offset-bottom, 0px) + var(--q-page-padding, 0px));
127
+ padding-bottom: calc(
128
+ max(constant(safe-area-inset-bottom), var(--q-page-offset-bottom, 0px)) +
129
+ var(--q-page-padding, 0px)
130
+ );
131
+ padding-bottom: calc(
132
+ max(env(safe-area-inset-bottom), var(--q-page-offset-bottom, 0px)) +
133
+ var(--q-page-padding, 0px)
134
+ );
135
+ }
136
+
137
+ /* Conteneur de pages (QPageContainer, modèle Quasar) : la cellule de contenu du
138
+ QLayout, colonne flex pour que la page qu'il contient remplisse la zone. */
139
+ .q-page-container {
140
+ position: relative;
141
+ display: flex;
142
+ flex-direction: column;
143
+ flex: 1 1 auto;
144
+ min-height: 0;
145
+ }
146
+
147
+ /* Compensation des barres `fixed` : seulement quand **aucune** page n'est rendue à
148
+ l'intérieur — sinon c'est `.q-page` qui s'en charge (jamais les deux). */
149
+ .q-page-container:not(:has(.q-page)) {
150
+ padding-top: var(--q-page-offset, 0px);
151
+ padding-bottom: var(--q-page-offset-bottom, 0px);
152
+ padding-bottom: calc(max(constant(safe-area-inset-bottom), var(--q-page-offset-bottom, 0px)));
153
+ padding-bottom: calc(max(env(safe-area-inset-bottom), var(--q-page-offset-bottom, 0px)));
113
154
  }
114
155
 
115
- .q-app:has(.q-header--fixed) .q-page {
116
- padding-top: 50px;
117
- padding-top: calc(constant(safe-area-inset-top) + 50px);
118
- padding-top: calc(env(safe-area-inset-top) + 50px);
156
+ /* Fallback sans mesure JS (SSR, barres pas encore observées) : 50px + safe-area.
157
+ Écrit dans la **variable** d'offset — et non en padding — pour que la hauteur
158
+ mesurée (style inline, lib/fixedLayout.ts) la remplace dès l'hydratation. */
159
+ .q-app:has(.q-header--fixed) :is(.q-page, .q-page-container) {
160
+ --q-page-offset: 50px;
161
+ --q-page-offset: calc(constant(safe-area-inset-top) + 50px);
162
+ --q-page-offset: calc(env(safe-area-inset-top) + 50px);
119
163
  }
120
164
 
121
- .q-app:has(.q-footer--fixed) .q-page {
122
- padding-bottom: 50px;
123
- padding-bottom: calc(constant(safe-area-inset-bottom) + 50px);
124
- padding-bottom: calc(env(safe-area-inset-bottom) + 50px);
165
+ .q-app:has(.q-footer--fixed) :is(.q-page, .q-page-container) {
166
+ --q-page-offset-bottom: 50px;
167
+ --q-page-offset-bottom: calc(constant(safe-area-inset-bottom) + 50px);
168
+ --q-page-offset-bottom: calc(env(safe-area-inset-bottom) + 50px);
125
169
  }
126
170
 
127
171
  /* ===== QLayout — matrice 3×3 pilotée par `view` =====
@@ -320,10 +364,6 @@ body {
320
364
  --muted: #1c2128;
321
365
  --border: rgb(255 255 255 / 0.12);
322
366
  --q-table-border: rgb(255 255 255 / 0.12);
323
- /* Opaque (4 % de blanc composé sur la surface) : un en-tête sticky ne doit pas
324
- laisser voir les lignes qui défilent dessous. Fallback avant color-mix. */
325
- --q-table-header-bg: #1f242b;
326
- --q-table-header-bg: color-mix(in srgb, #fff 4%, var(--card, #161b22));
327
367
  --q-table-hover-bg: rgb(255 255 255 / 0.06);
328
368
  --q-sidebar-border: rgb(255 255 255 / 0.12);
329
369
  --q-translucent-bg: #0f1115;
@@ -1623,14 +1663,14 @@ html.dark body {
1623
1663
  height: 55%;
1624
1664
  }
1625
1665
 
1626
- /* ===== Translucide (frosted glass) — partagé header/footer/back-header/bottom-sheet =====
1666
+ /* ===== Translucide (frosted glass) — partagé header/footer/back-header =====
1627
1667
  translucent="70" → 70% opaque + flou derrière (backdrop-filter).
1628
- Les composants posent --q-translucent-opacity (défaut 70%) et la classe --translucent. */
1668
+ Les composants posent --q-translucent-opacity (défaut 70%) et la classe --translucent.
1669
+ Les feuilles (bottom sheet, country picker) ont leur propre règle dans leur section :
1670
+ leur fond de base est déclaré **après** ce bloc, donc plus loin dans le fichier. */
1629
1671
  .q-header--translucent,
1630
1672
  .q-footer--translucent,
1631
- .q-back-header--translucent,
1632
- .q-bottom-sheet__panel--translucent,
1633
- .q-country-picker__sheet--translucent {
1673
+ .q-back-header--translucent {
1634
1674
  background-color: color-mix(
1635
1675
  in srgb,
1636
1676
  var(--q-translucent-bg, #fff) var(--q-translucent-opacity, 70%),
@@ -3218,10 +3258,10 @@ html.dark body {
3218
3258
  /* ===== QTable ===== */
3219
3259
  .q-table {
3220
3260
  --q-table-border: rgb(0 0 0 / 0.1);
3221
- /* Opaque : 3 % de noir **composé sur la surface** (un en-tête sticky laissait
3222
- sinon voir les lignes qui défilent dessous). Fallback avant color-mix. */
3223
- --q-table-header-bg: #f7f7f7;
3224
- --q-table-header-bg: color-mix(in srgb, #000 3%, #fff);
3261
+ /* En-tête : la MÊME surface que le tableau (blanc en clair, carte en sombre) — il se
3262
+ distingue par sa bordure basse, pas par un fond grisé. Opaque par construction,
3263
+ donc un en-tête collant ne laisse pas voir les lignes qui défilent dessous. */
3264
+ --q-table-header-bg: var(--card, #fff);
3225
3265
  --q-table-hover-bg: rgb(0 0 0 / 0.04);
3226
3266
 
3227
3267
  position: relative;
@@ -3245,8 +3285,8 @@ html.dark body {
3245
3285
 
3246
3286
  .q-table--dark {
3247
3287
  --q-table-border: rgb(255 255 255 / 0.15);
3248
- --q-table-header-bg: #2b2b2b;
3249
- --q-table-header-bg: color-mix(in srgb, #fff 6%, #1d1d1d);
3288
+ /* = la surface du tableau dans cette variante (cf. `background-color` ci-dessous) */
3289
+ --q-table-header-bg: #1d1d1d;
3250
3290
  --q-table-hover-bg: rgb(255 255 255 / 0.06);
3251
3291
  background-color: #1d1d1d;
3252
3292
  color: #fff;
@@ -4449,11 +4489,15 @@ a.q-breadcrumbs__el:hover {
4449
4489
  transition: background-color 0.15s ease;
4450
4490
  }
4451
4491
 
4452
- .q-sidebar__menu-button:hover {
4492
+ /* Hover : uniquement sur un item NON actif — sans le `:not(...)`, la règle `:hover`
4493
+ (spécificité 0,2,0) écrasait l'état actif `.q-sidebar__menu-button--active`
4494
+ (0,1,0) : au survol, le fond passait au gris et le texte blanc du primaire devenait
4495
+ illisible. Un item actif garde donc son apparence au survol. */
4496
+ .q-sidebar__menu-button:hover:not(.q-sidebar__menu-button--active) {
4453
4497
  background-color: rgb(0 0 0 / 0.05);
4454
4498
  }
4455
4499
 
4456
- .q-sidebar--dark .q-sidebar__menu-button:hover {
4500
+ .q-sidebar--dark .q-sidebar__menu-button:hover:not(.q-sidebar__menu-button--active) {
4457
4501
  background-color: rgb(255 255 255 / 0.08);
4458
4502
  }
4459
4503
 
@@ -5150,6 +5194,20 @@ a.q-breadcrumbs__el:hover {
5150
5194
  background-color: rgb(0 0 0 / 0.5);
5151
5195
  }
5152
5196
 
5197
+ /* Mode « seamless » : pas de backdrop — le fond n'est pas assombri et les clics le
5198
+ traversent (le panneau reste interactif, le clic à côté ne ferme plus).
5199
+ L'ombre est adoucie : sans fond assombri derrière, une ombre forte ferait une bande
5200
+ noire sous le panneau. `--q-bs-shadow` reste surchargeable (prop `shadow`). */
5201
+ .q-bottom-sheet__overlay--seamless {
5202
+ background-color: transparent;
5203
+ pointer-events: none;
5204
+ }
5205
+
5206
+ .q-bottom-sheet__overlay--seamless .q-bottom-sheet__panel {
5207
+ pointer-events: auto;
5208
+ --q-bs-shadow: 0 -2px 12px rgb(0 0 0 / 0.08);
5209
+ }
5210
+
5153
5211
  .q-bottom-sheet__panel {
5154
5212
  display: flex;
5155
5213
  flex-direction: column;
@@ -5159,7 +5217,8 @@ a.q-breadcrumbs__el:hover {
5159
5217
  background-color: #fff;
5160
5218
  color: inherit;
5161
5219
  border-radius: 16px 16px 0 0;
5162
- box-shadow: 0 -4px 24px rgb(0 0 0 / 0.2);
5220
+ /* Ombre paramétrable : prop `shadow` (« none », valeur CSS) ou `--q-bs-shadow` */
5221
+ box-shadow: var(--q-bs-shadow, 0 -4px 24px rgb(0 0 0 / 0.2));
5163
5222
  transition: transform 0.25s ease;
5164
5223
  padding-bottom: 0;
5165
5224
  padding-bottom: constant(safe-area-inset-bottom);
@@ -5179,6 +5238,50 @@ a.q-breadcrumbs__el:hover {
5179
5238
  transition: none;
5180
5239
  }
5181
5240
 
5241
+ /* Surfaces translucides du panneau : déclarées **après** `.q-bottom-sheet__panel` — à
5242
+ spécificité égale, c'est l'ordre du fichier qui tranche (elles doivent gagner sur
5243
+ `background-color: #fff`). `glass` vient en dernier : il prime sur `translucent`. */
5244
+ .q-bottom-sheet__panel--translucent {
5245
+ background-color: color-mix(
5246
+ in srgb,
5247
+ var(--q-translucent-bg, #fff) var(--q-translucent-opacity, 70%),
5248
+ transparent
5249
+ );
5250
+ backdrop-filter: blur(var(--q-translucent-blur, 12px)) saturate(1.4);
5251
+ -webkit-backdrop-filter: blur(var(--q-translucent-blur, 12px)) saturate(1.4);
5252
+ }
5253
+
5254
+ .q-bottom-sheet__panel--glass {
5255
+ --q-glass-bg: rgb(255 255 255 / 0.14);
5256
+ --q-glass-blur: 20px;
5257
+ background-color: var(--q-glass-bg);
5258
+ border: 1px solid rgb(255 255 255 / 0.22);
5259
+ border-bottom: 0;
5260
+ -webkit-backdrop-filter: blur(var(--q-glass-blur)) saturate(1.6);
5261
+ backdrop-filter: blur(var(--q-glass-blur)) saturate(1.6);
5262
+ }
5263
+
5264
+ .dark .q-bottom-sheet__panel--glass {
5265
+ --q-glass-bg: rgb(255 255 255 / 0.07);
5266
+ border-color: rgb(255 255 255 / 0.12);
5267
+ }
5268
+
5269
+ /* Mode « points d'ancrage » : la hauteur vient du point d'ancrage courant — une fraction
5270
+ de la hauteur de vue posée en variable par le composant. Le contenu se réagence à chaque
5271
+ changement et reste défilable (`__body`), y compris sur un petit point d'ancrage. */
5272
+ .q-bottom-sheet__panel--breakpoints {
5273
+ max-height: none;
5274
+ height: calc(var(--q-bs-breakpoint, 1) * 100vh);
5275
+ height: calc(var(--q-bs-breakpoint, 1) * 100dvh);
5276
+ transition:
5277
+ height 0.25s ease,
5278
+ transform 0.25s ease;
5279
+ }
5280
+
5281
+ .q-bottom-sheet__panel--breakpoints.q-bottom-sheet__panel--dragging {
5282
+ transition: none;
5283
+ }
5284
+
5182
5285
  .q-bottom-sheet__handle {
5183
5286
  display: flex;
5184
5287
  align-items: center;
@@ -5203,6 +5306,7 @@ a.q-breadcrumbs__el:hover {
5203
5306
  .q-bottom-sheet__body {
5204
5307
  display: flex;
5205
5308
  flex-direction: column;
5309
+ flex: 1 1 auto; /* remplit le panneau (points d'ancrage) — inerte sinon */
5206
5310
  min-height: 0;
5207
5311
  overflow-y: auto;
5208
5312
  }
@@ -6031,6 +6135,17 @@ a.q-breadcrumbs__el:hover {
6031
6135
  color: var(--foreground);
6032
6136
  }
6033
6137
 
6138
+ /* Translucide : après le fond de base (même spécificité → l'ordre tranche) */
6139
+ .q-country-picker__sheet--translucent {
6140
+ background-color: color-mix(
6141
+ in srgb,
6142
+ var(--q-translucent-bg, #fff) var(--q-translucent-opacity, 70%),
6143
+ transparent
6144
+ );
6145
+ backdrop-filter: blur(var(--q-translucent-blur, 12px)) saturate(1.4);
6146
+ -webkit-backdrop-filter: blur(var(--q-translucent-blur, 12px)) saturate(1.4);
6147
+ }
6148
+
6034
6149
  /* poignée de drag (mode sheet) : swipe vers le bas pour fermer */
6035
6150
  .q-country-picker__handle {
6036
6151
  display: flex;
@@ -7311,7 +7426,7 @@ a.q-breadcrumbs__el:hover {
7311
7426
  width: 100%;
7312
7427
  max-width: var(--q-container-max, 1200px);
7313
7428
  margin-inline: auto;
7314
- padding-inline: var(--q-container-pad, 16px);
7429
+ padding-inline: var(--q-container-pad, 12px);
7315
7430
  }
7316
7431
 
7317
7432
  .q-container--fluid {
@@ -9055,899 +9170,190 @@ a.q-breadcrumbs__el:hover {
9055
9170
  }
9056
9171
  }
9057
9172
 
9058
- /* ===== QTiptap ===== */
9059
- .q-tiptap {
9060
- --q-tiptap-bg: #fff;
9061
- --q-tiptap-toolbar-bg: #f6f7f9;
9062
- --q-tiptap-border: var(--border, rgb(0 0 0 / 0.12));
9063
- --q-tiptap-text: var(--foreground, #1d1d1d);
9064
- /* ⚠ --muted est un token de FOND (bg grisé shadcn), pas une couleur de texte
9065
- → icônes/secondary en gris texte explicite */
9066
- --q-tiptap-muted: #8b93a1;
9067
- --q-tiptap-primary: var(--primary, #1976d2);
9068
- --q-tiptap-radius: var(--q-radius, 12px);
9069
- --q-tiptap-code-bg: rgb(0 0 0 / 0.06);
9070
- --q-tiptap-quote-bg: rgb(25 118 210 / 0.07);
9173
+ /* ===== QBar ===== */
9174
+ .q-bar {
9175
+ display: flex;
9176
+ align-items: center;
9177
+ gap: 10px;
9178
+ min-height: 40px;
9179
+ padding: 0 12px;
9180
+ font-size: 13px;
9181
+ line-height: 1.4;
9182
+ color: var(--foreground, #1d1d1d);
9183
+ background: transparent;
9184
+ user-select: none;
9185
+ }
9186
+ .q-bar--dense {
9187
+ gap: 8px;
9188
+ min-height: 32px;
9189
+ padding: 0 10px;
9190
+ font-size: 12px;
9191
+ }
9192
+ .q-bar--dark {
9193
+ color: #ececef;
9194
+ background: #1d1d1d;
9195
+ }
9196
+ .q-bar > * {
9197
+ flex-shrink: 0;
9198
+ }
9199
+ .q-bar__dot {
9200
+ width: 12px;
9201
+ height: 12px;
9202
+ border-radius: 50%;
9203
+ }
9204
+
9205
+ /* ===== QSpreadsheet (tableur type Excel) ===== */
9206
+ .q-spreadsheet {
9207
+ --q-spreadsheet-border: rgb(0 0 0 / 0.1);
9208
+ /* Opaque (cf. QTable : entêtes sticky des colonnes/lignes) */
9209
+ --q-spreadsheet-header-bg: #f7f7f7;
9210
+ --q-spreadsheet-header-bg: color-mix(in srgb, #000 3%, #fff);
9211
+ --q-spreadsheet-row-alt: rgb(0 0 0 / 0.015);
9212
+ --q-spreadsheet-range: rgb(25 118 210 / 0.1);
9213
+ --q-spreadsheet-range-brd: var(--primary, #1976d2);
9214
+
9215
+ position: relative;
9071
9216
  display: flex;
9072
9217
  flex-direction: column;
9218
+ width: 100%;
9073
9219
  overflow: hidden;
9074
- border: 1px solid var(--q-tiptap-border);
9075
- border-radius: var(--q-tiptap-radius);
9076
- background-color: var(--q-tiptap-bg);
9077
- color: var(--q-tiptap-text);
9078
- }
9079
- .dark .q-tiptap {
9080
- --q-tiptap-bg: #1e1e22;
9081
- --q-tiptap-toolbar-bg: #27272c;
9082
- --q-tiptap-border: rgb(255 255 255 / 0.12);
9083
- --q-tiptap-text: #ececef;
9084
- --q-tiptap-muted: #9aa2b1;
9085
- --q-tiptap-code-bg: rgb(255 255 255 / 0.08);
9086
- --q-tiptap-quote-bg: rgb(25 118 210 / 0.18);
9087
- }
9088
- .q-tiptap--filled {
9089
- --q-tiptap-bg: #f6f7f9;
9090
- --q-tiptap-toolbar-bg: transparent;
9091
- border-color: transparent;
9092
- box-shadow: inset 0 0 0 1px var(--q-tiptap-border);
9093
- }
9094
- .dark .q-tiptap--filled {
9095
- --q-tiptap-bg: #26262b;
9096
- --q-tiptap-toolbar-bg: transparent;
9097
- box-shadow: inset 0 0 0 1px rgb(255 255 255 / 0.1);
9220
+ border-radius: var(--q-radius, 0);
9221
+ background: #fff;
9222
+ color: var(--foreground, #1d1d1d);
9223
+ font-size: 13px;
9098
9224
  }
9099
- .q-tiptap--square {
9100
- border-radius: 0;
9225
+ .q-spreadsheet--bordered {
9226
+ border: 1px solid var(--q-spreadsheet-border);
9101
9227
  }
9102
- .q-tiptap--disable {
9103
- opacity: 0.7;
9228
+ .q-spreadsheet--flat {
9229
+ box-shadow: none;
9104
9230
  }
9105
- .q-tiptap--disable .q-tiptap__body {
9231
+ .q-spreadsheet--disable {
9232
+ opacity: 0.6;
9106
9233
  pointer-events: none;
9234
+ user-select: none;
9235
+ }
9236
+ .q-spreadsheet--dark,
9237
+ .dark .q-spreadsheet {
9238
+ --q-spreadsheet-border: rgb(255 255 255 / 0.12);
9239
+ --q-spreadsheet-header-bg: #22262d;
9240
+ --q-spreadsheet-header-bg: color-mix(in srgb, #fff 5%, var(--card, #1d1d1d));
9241
+ --q-spreadsheet-row-alt: rgb(255 255 255 / 0.02);
9242
+ background: var(--card, #1d1d1d);
9243
+ color: var(--foreground, #ececef);
9107
9244
  }
9108
9245
 
9109
- /* — barre d'outils — */
9110
- .q-tiptap__toolbar {
9246
+ /* ─── Barre d'outils ─── */
9247
+ .q-spreadsheet__toolbar {
9111
9248
  display: flex;
9112
9249
  align-items: center;
9113
9250
  flex-wrap: wrap;
9114
- gap: 2px;
9115
- padding: 4px 6px;
9116
- background-color: var(--q-tiptap-toolbar-bg);
9117
- border-bottom: 1px solid var(--q-tiptap-border);
9251
+ gap: 4px;
9252
+ padding: 6px 8px;
9253
+ border-bottom: 1px solid var(--q-spreadsheet-border);
9254
+ background: var(--q-spreadsheet-header-bg);
9118
9255
  }
9119
- .q-tiptap__group {
9120
- display: flex;
9256
+ .q-spreadsheet__sheet-name {
9257
+ display: inline-flex;
9121
9258
  align-items: center;
9122
- gap: 2px;
9259
+ gap: 6px;
9260
+ margin-right: 6px;
9261
+ padding: 3px 10px;
9262
+ font-size: 12px;
9263
+ font-weight: 600;
9264
+ color: var(--foreground, #1d1d1d);
9265
+ background: rgb(25 118 210 / 0.08);
9266
+ border-radius: 6px;
9267
+ white-space: nowrap;
9123
9268
  }
9124
- .q-tiptap__group + .q-tiptap__group {
9125
- margin-left: 6px;
9126
- padding-left: 6px;
9127
- border-left: 1px solid var(--q-tiptap-border);
9269
+ .q-spreadsheet__tb-group {
9270
+ display: inline-flex;
9271
+ align-items: center;
9272
+ gap: 2px;
9273
+ padding: 0 4px;
9274
+ border-left: 1px solid var(--q-spreadsheet-border);
9128
9275
  }
9129
- .q-tiptap__tool {
9276
+ .q-spreadsheet__tool {
9277
+ position: relative; /* support du badge « + » des boutons d'ajout */
9130
9278
  display: inline-flex;
9131
9279
  align-items: center;
9132
9280
  justify-content: center;
9133
- width: 30px;
9134
- height: 30px;
9281
+ width: 28px;
9282
+ height: 28px;
9135
9283
  padding: 0;
9136
9284
  border: 0;
9137
- border-radius: 8px;
9285
+ border-radius: 6px;
9138
9286
  background: transparent;
9139
- color: var(--q-tiptap-muted);
9287
+ color: var(--foreground, #1d1d1d);
9140
9288
  cursor: pointer;
9141
- transition:
9142
- background-color 0.15s ease,
9143
- color 0.15s ease;
9144
- }
9145
- .q-tiptap__tool svg {
9146
- width: 17px;
9147
- height: 17px;
9289
+ transition: background-color 0.15s ease;
9148
9290
  }
9149
- .q-tiptap__tool:hover:not(:disabled) {
9150
- background-color: rgb(0 0 0 / 0.06);
9151
- color: var(--q-tiptap-text);
9291
+ .q-spreadsheet__tool:hover:not(:disabled) {
9292
+ background: rgb(25 118 210 / 0.1);
9293
+ color: var(--primary, #1976d2);
9152
9294
  }
9153
- .dark .q-tiptap__tool:hover:not(:disabled) {
9154
- background-color: rgb(255 255 255 / 0.08);
9295
+ .q-spreadsheet__tool:disabled {
9296
+ opacity: 0.35;
9297
+ cursor: default;
9155
9298
  }
9156
- .q-tiptap__tool--active {
9157
- background-color: rgb(25 118 210 / 0.14);
9158
- background-color: color-mix(in srgb, var(--q-tiptap-primary) 14%, transparent);
9159
- color: var(--q-tiptap-primary);
9299
+ .q-spreadsheet__tool .iconify {
9300
+ font-size: 17px;
9160
9301
  }
9161
- .q-tiptap__tool:disabled {
9162
- opacity: 0.4;
9163
- cursor: not-allowed;
9302
+
9303
+ /* Badge « + » des boutons d'ajout de ligne / colonne : le glyphe de fond dit
9304
+ l'axe (rows / columns), le + dit l'action d'ajout — sans lui, l'icône seule se
9305
+ lit comme « des lignes existantes », pas comme « ajouter une ligne ».
9306
+ (Après `.iconify` à spécificité égale → c'est cette taille qui gagne.) */
9307
+ .q-spreadsheet__tool .q-spreadsheet__tool-badge {
9308
+ position: absolute;
9309
+ right: 1px;
9310
+ bottom: 1px;
9311
+ padding: 1px;
9312
+ border-radius: 50%;
9313
+ font-size: 11px;
9314
+ background: var(--q-spreadsheet-bg, #fff);
9315
+ color: var(--primary, #1976d2);
9316
+ box-shadow: 0 0 0 1px var(--q-spreadsheet-border);
9164
9317
  }
9165
- .q-tiptap--dense .q-tiptap__tool {
9166
- width: 26px;
9167
- height: 26px;
9318
+ .q-spreadsheet--dense .q-spreadsheet__tool {
9319
+ width: 24px;
9320
+ height: 24px;
9168
9321
  }
9169
9322
 
9170
- /* — zone d'édition — */
9171
- .q-tiptap__body {
9172
- position: relative;
9173
- flex: 1;
9174
- min-width: 0;
9175
- }
9176
- .q-tiptap__editor {
9323
+ /* ─── Viewport / table ─── */
9324
+ .q-spreadsheet__viewport {
9177
9325
  position: relative;
9178
- z-index: 1;
9179
- min-height: 180px;
9180
- }
9181
- .q-tiptap__editor > div,
9182
- .q-tiptap__editor .ProseMirror {
9183
- min-height: inherit;
9184
- }
9185
- .q-tiptap__editor .ProseMirror {
9186
- padding: var(--q-tiptap-content-padding, 5%);
9326
+ overflow: auto;
9187
9327
  outline: none;
9188
- font-size: 14px;
9189
- line-height: 1.7;
9190
- color: var(--q-tiptap-text);
9191
- caret-color: var(--q-tiptap-primary);
9192
- word-wrap: break-word;
9193
9328
  }
9194
-
9195
- /* placeholder (visible quand le document est vide) — aligné sur le padding
9196
- par défaut (5% sur les 4 côtés) */
9197
- .q-tiptap__placeholder {
9198
- position: absolute;
9199
- z-index: 0;
9200
- top: 5%;
9201
- left: 5%;
9202
- right: 5%;
9203
- font-style: italic;
9204
- font-size: 14px;
9205
- line-height: 1.7;
9206
- color: var(--q-tiptap-muted);
9207
- pointer-events: none;
9208
- white-space: pre-wrap;
9329
+ .q-spreadsheet__table {
9330
+ /* PAS de `min-width: 100%` : avec `table-layout: fixed`, un tableau étiré au-delà
9331
+ de la somme de ses colonnes RÉPARTIT l'espace libre sur toutes les colonnes
9332
+ (proportionnellement) — la gouttière des numéros de ligne (34px) gonflait donc
9333
+ jusqu'à ~70px sur une grille étroite, et les largeurs configurées de colonnes
9334
+ étaient ignorées. Ici le tableau fait exactement la somme des colonnes ;
9335
+ l'espace restant à droite reste vide (comportement tableur). */
9336
+ width: max-content;
9337
+ border-collapse: separate;
9338
+ border-spacing: 0;
9339
+ table-layout: fixed;
9340
+ font-size: 13px;
9209
9341
  }
9210
9342
 
9211
- /* — typographie du contenu édité (ProseMirror) — */
9212
- .q-tiptap__editor .ProseMirror p {
9213
- margin: 0 0 0.6em;
9214
- }
9215
- .q-tiptap__editor .ProseMirror h1,
9216
- .q-tiptap__editor .ProseMirror h2,
9217
- .q-tiptap__editor .ProseMirror h3 {
9218
- margin: 0.8em 0 0.4em;
9219
- color: var(--q-tiptap-text);
9220
- font-weight: 700;
9221
- line-height: 1.3;
9222
- }
9223
- .q-tiptap__editor .ProseMirror h1 {
9224
- font-size: 1.55em;
9225
- }
9226
- .q-tiptap__editor .ProseMirror h2 {
9227
- font-size: 1.3em;
9228
- }
9229
- .q-tiptap__editor .ProseMirror h3 {
9230
- font-size: 1.1em;
9343
+ /* Grille sans aucune colonne : un seul message → il doit occuper toute la largeur */
9344
+ .q-spreadsheet__table--empty {
9345
+ min-width: 100%;
9231
9346
  }
9232
- .q-tiptap__editor .ProseMirror > *:first-child {
9233
- margin-top: 0;
9234
- }
9235
- .q-tiptap__editor .ProseMirror ul,
9236
- .q-tiptap__editor .ProseMirror ol {
9237
- margin: 0 0 0.8em;
9238
- /* listes décalées d'un retrait (margin-left) ; petit padding pour les
9239
- marqueurs (puces / numéros) */
9240
- margin-left: 1.5em;
9241
- padding-left: 0.4em;
9242
- }
9243
- /* listes imbriquées : retrait réduit pour rester lisible */
9244
- .q-tiptap__editor .ProseMirror ul ul,
9245
- .q-tiptap__editor .ProseMirror ul ol,
9246
- .q-tiptap__editor .ProseMirror ol ul,
9247
- .q-tiptap__editor .ProseMirror ol ol {
9248
- margin-left: 0.75em;
9249
- }
9250
- /* Tailwind preflight met ul/ol { list-style: none } → restaurer les marqueurs */
9251
- .q-tiptap__editor .ProseMirror ul {
9252
- list-style-type: disc;
9253
- }
9254
- .q-tiptap__editor .ProseMirror ol {
9255
- list-style-type: decimal;
9256
- }
9257
- .q-tiptap__editor .ProseMirror ul ul {
9258
- list-style-type: circle;
9259
- }
9260
- .q-tiptap__editor .ProseMirror ul ul ul {
9261
- list-style-type: square;
9262
- }
9263
- .q-tiptap__editor .ProseMirror li {
9264
- margin: 0.15em 0;
9265
- }
9266
- .q-tiptap__editor .ProseMirror li p {
9267
- margin: 0;
9268
- }
9269
- .q-tiptap__editor .ProseMirror blockquote {
9270
- margin: 0.6em 0;
9271
- padding: 6px 12px;
9272
- border-left: 3px solid var(--q-tiptap-primary);
9273
- border-radius: 0 8px 8px 0;
9274
- background: var(--q-tiptap-quote-bg);
9275
- }
9276
- .q-tiptap__editor .ProseMirror blockquote p {
9277
- margin: 0;
9278
- }
9279
- .q-tiptap__editor .ProseMirror pre {
9280
- margin: 0.6em 0;
9281
- padding: 10px 12px;
9282
- overflow-x: auto;
9283
- border: 1px solid var(--q-tiptap-border);
9284
- border-radius: 8px;
9285
- background: var(--q-tiptap-code-bg);
9286
- font-size: 0.92em;
9287
- }
9288
- .q-tiptap__editor .ProseMirror pre code {
9289
- background: none;
9290
- padding: 0;
9291
- font-size: 1em;
9292
- }
9293
- .q-tiptap__editor .ProseMirror code {
9294
- padding: 0.1em 0.35em;
9295
- border-radius: 4px;
9296
- background: var(--q-tiptap-code-bg);
9297
- font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
9298
- font-size: 0.92em;
9299
- }
9300
- .q-tiptap__editor .ProseMirror a {
9301
- color: var(--q-tiptap-primary);
9302
- text-decoration: underline;
9303
- cursor: pointer;
9304
- }
9305
- .q-tiptap__editor .ProseMirror hr {
9306
- margin: 0.8em 0;
9307
- border: none;
9308
- border-top: 1px solid var(--q-tiptap-border);
9309
- }
9310
- .q-tiptap__editor .ProseMirror img {
9311
- display: block;
9312
- max-width: 100%;
9313
- height: auto;
9314
- margin: 0.6em auto;
9315
- border-radius: 8px;
9316
- box-shadow: 0 0 0 1px var(--q-tiptap-border);
9317
- }
9318
- .q-tiptap__editor .ProseMirror .ProseMirror-selectednode {
9319
- outline: 2px solid var(--q-tiptap-primary);
9320
- border-radius: 4px;
9321
- }
9322
-
9323
- /* — couleur de texte (toolbar + palette popover) — */
9324
- .q-tiptap__tool--color {
9325
- position: relative;
9326
- }
9327
- .q-tiptap__color-bar {
9328
- position: absolute;
9329
- right: 7px;
9330
- bottom: 4px;
9331
- left: 7px;
9332
- height: 3px;
9333
- border-radius: 2px;
9334
- pointer-events: none;
9335
- }
9336
- .q-tiptap__tool--disabled {
9337
- opacity: 0.4;
9338
- cursor: not-allowed;
9339
- }
9340
- .q-tiptap__tool--color:focus-visible {
9341
- box-shadow: inset 0 0 0 2px var(--q-tiptap-primary);
9342
- }
9343
- .q-tiptap__palette {
9344
- position: fixed;
9345
- z-index: 4000;
9346
- width: 224px;
9347
- padding: 10px;
9348
- background: var(--q-tiptap-bg);
9349
- border: 1px solid var(--q-tiptap-border);
9350
- border-radius: 12px;
9351
- box-shadow: 0 14px 34px rgb(0 0 0 / 0.18);
9352
- }
9353
- .q-tiptap__palette-title {
9354
- margin: 0 2px 8px;
9355
- font-size: 11px;
9356
- font-weight: 600;
9357
- text-transform: uppercase;
9358
- letter-spacing: 0.05em;
9359
- color: var(--q-tiptap-muted);
9360
- }
9361
- .q-tiptap__palette-swatches {
9362
- display: grid;
9363
- grid-template-columns: repeat(7, 1fr);
9364
- gap: 6px;
9365
- }
9366
- .q-tiptap__swatch {
9367
- position: relative;
9368
- width: 100%;
9369
- aspect-ratio: 1;
9370
- padding: 0;
9371
- border: 1px solid var(--q-tiptap-border);
9372
- border-radius: 6px;
9373
- background: transparent;
9374
- cursor: pointer;
9375
- transition: transform 0.1s ease;
9376
- }
9377
- .q-tiptap__swatch:hover {
9378
- transform: scale(1.12);
9379
- }
9380
- .q-tiptap__swatch--active {
9381
- box-shadow:
9382
- 0 0 0 2px var(--q-tiptap-bg),
9383
- 0 0 0 4px var(--q-tiptap-primary);
9384
- }
9385
- .q-tiptap__swatch--none {
9386
- background: #fff;
9387
- background-image: linear-gradient(
9388
- to top right,
9389
- transparent calc(50% - 1px),
9390
- #e5484d calc(50% - 1px),
9391
- #e5484d calc(50% + 1px),
9392
- transparent calc(50% + 1px)
9393
- );
9394
- }
9395
- .q-tiptap__palette-custom {
9396
- display: flex;
9397
- align-items: center;
9398
- gap: 8px;
9399
- margin-top: 10px;
9400
- padding-top: 10px;
9401
- border-top: 1px solid var(--q-tiptap-border);
9402
- font-size: 12px;
9403
- color: var(--q-tiptap-muted);
9404
- cursor: pointer;
9405
- }
9406
- .q-tiptap__palette-custom input[type="color"] {
9407
- width: 26px;
9408
- height: 26px;
9409
- padding: 0;
9410
- border: 1px solid var(--q-tiptap-border);
9411
- border-radius: 6px;
9412
- background: none;
9413
- cursor: pointer;
9414
- }
9415
- .q-tiptap__palette-custom input[type="color"]::-webkit-color-swatch-wrapper {
9416
- padding: 2px;
9417
- }
9418
- .q-tiptap__palette-custom input[type="color"]::-webkit-color-swatch {
9419
- border: none;
9420
- border-radius: 4px;
9421
- }
9422
-
9423
- /* — mentions (@) : puce dans le document + popup de suggestion — */
9424
- .q-tiptap__editor .ProseMirror span[data-type="mention"] {
9425
- display: inline-block;
9426
- padding: 0 5px;
9427
- margin: 0 1px;
9428
- border-radius: 6px;
9429
- background: rgb(25 118 210 / 0.12);
9430
- background: color-mix(in srgb, var(--q-tiptap-primary) 14%, transparent);
9431
- color: var(--q-tiptap-primary);
9432
- font-weight: 500;
9433
- white-space: nowrap;
9434
- }
9435
- .q-tiptap__mention-list {
9436
- display: flex;
9437
- flex-direction: column;
9438
- gap: 2px;
9439
- width: 220px;
9440
- max-height: 240px;
9441
- overflow-y: auto;
9442
- padding: 4px;
9443
- background: var(--q-tiptap-bg);
9444
- border: 1px solid var(--q-tiptap-border);
9445
- border-radius: 10px;
9446
- box-shadow: 0 12px 28px rgb(0 0 0 / 0.18);
9447
- }
9448
- .q-tiptap__mention-item {
9449
- display: flex;
9450
- align-items: center;
9451
- width: 100%;
9452
- padding: 6px 10px;
9453
- border: 0;
9454
- border-radius: 7px;
9455
- background: transparent;
9456
- color: var(--q-tiptap-text);
9457
- font-size: 13px;
9458
- text-align: left;
9459
- cursor: pointer;
9460
- white-space: nowrap;
9461
- overflow: hidden;
9462
- text-overflow: ellipsis;
9463
- }
9464
- .q-tiptap__mention-item:hover,
9465
- .q-tiptap__mention-item--active {
9466
- background: rgb(25 118 210 / 0.12);
9467
- background: color-mix(in srgb, var(--q-tiptap-primary) 14%, transparent);
9468
- color: var(--q-tiptap-primary);
9469
- }
9470
- .q-tiptap__mention-empty {
9471
- padding: 8px 10px;
9472
- font-size: 13px;
9473
- color: var(--q-tiptap-muted);
9474
- }
9475
-
9476
- /* — bubble menu : barre contextuelle de formatage (sélection) — */
9477
- .q-tiptap__bubble {
9478
- display: flex;
9479
- align-items: center;
9480
- gap: 2px;
9481
- width: max-content;
9482
- padding: 4px;
9483
- background: var(--q-tiptap-bg);
9484
- border: 1px solid var(--q-tiptap-border);
9485
- border-radius: 12px;
9486
- box-shadow: 0 10px 28px rgb(0 0 0 / 0.16);
9487
- }
9488
- .q-tiptap__bubble-btn {
9489
- display: inline-flex;
9490
- align-items: center;
9491
- justify-content: center;
9492
- width: 28px;
9493
- height: 28px;
9494
- padding: 0;
9495
- border: 0;
9496
- border-radius: 8px;
9497
- background: transparent;
9498
- color: var(--q-tiptap-muted);
9499
- cursor: pointer;
9500
- transition:
9501
- background-color 0.15s ease,
9502
- color 0.15s ease;
9503
- }
9504
- .q-tiptap__bubble-btn svg {
9505
- width: 15px;
9506
- height: 15px;
9507
- }
9508
- .q-tiptap__bubble-btn:hover:not(:disabled) {
9509
- background-color: rgb(0 0 0 / 0.06);
9510
- color: var(--q-tiptap-text);
9511
- }
9512
- .dark .q-tiptap__bubble-btn:hover:not(:disabled) {
9513
- background-color: rgb(255 255 255 / 0.08);
9514
- }
9515
- .q-tiptap__bubble-btn--active {
9516
- background-color: rgb(25 118 210 / 0.14);
9517
- background-color: color-mix(in srgb, var(--q-tiptap-primary) 14%, transparent);
9518
- color: var(--q-tiptap-primary);
9519
- }
9520
- .q-tiptap__bubble-btn:disabled {
9521
- opacity: 0.4;
9522
- cursor: not-allowed;
9523
- }
9524
- .q-tiptap__bubble-sep {
9525
- width: 1px;
9526
- height: 16px;
9527
- margin: 0 3px;
9528
- background: var(--q-tiptap-border);
9529
- }
9530
- .q-tiptap__bubble .q-tiptap__color-bar {
9531
- right: 6px;
9532
- bottom: 3px;
9533
- left: 6px;
9534
- }
9535
-
9536
- /* — drag handle : poignée de déplacement des blocs — */
9537
- .q-tiptap__drag-handle {
9538
- display: inline-flex;
9539
- align-items: center;
9540
- justify-content: center;
9541
- width: 20px;
9542
- height: 20px;
9543
- /* espace minimum entre l'icône et l'élément dragué (≥ 5% de la largeur de
9544
- l'éditeur, relatif au conteneur positionné .q-tiptap__body) */
9545
- margin-right: 5%;
9546
- color: var(--q-tiptap-muted);
9547
- cursor: grab;
9548
- border-radius: 6px;
9549
- transition: color 0.15s ease, background-color 0.15s ease;
9550
- }
9551
- .q-tiptap__drag-handle:hover {
9552
- color: var(--q-tiptap-primary);
9553
- background: rgb(25 118 210 / 0.12);
9554
- background: color-mix(in srgb, var(--q-tiptap-primary) 14%, transparent);
9555
- }
9556
- .q-tiptap__drag-handle:active {
9557
- cursor: grabbing;
9558
- }
9559
- .q-tiptap__drag-handle svg {
9560
- width: 16px;
9561
- height: 16px;
9562
- }
9563
-
9564
- /* Les popups (mentions, palette, bubble) sont montés HORS de .q-tiptap
9565
- (Teleport / body via props.mount) → ils n'héritent PAS de --q-tiptap-*.
9566
- On leur redéfinit leurs variables localement : fond blanc en light
9567
- (--card), sombre via html.dark. */
9568
- .q-tiptap__mention-list,
9569
- .q-tiptap__palette,
9570
- .q-tiptap__bubble,
9571
- .q-tiptap__table-menu {
9572
- --q-tiptap-bg: var(--card, #ffffff);
9573
- --q-tiptap-border: var(--border, rgb(0 0 0 / 0.12));
9574
- --q-tiptap-text: var(--foreground, #1d1d1d);
9575
- --q-tiptap-muted: #8b93a1;
9576
- --q-tiptap-primary: var(--primary, #1976d2);
9577
- }
9578
- .dark .q-tiptap__mention-list,
9579
- .dark .q-tiptap__palette,
9580
- .dark .q-tiptap__bubble,
9581
- .dark .q-tiptap__table-menu {
9582
- --q-tiptap-bg: #1e1e22;
9583
- --q-tiptap-border: rgb(255 255 255 / 0.12);
9584
- --q-tiptap-text: #ececef;
9585
- --q-tiptap-muted: #9aa2b1;
9586
- --q-tiptap-primary: var(--primary, #1976d2);
9587
- }
9588
-
9589
- /* — sélecteur de taille de police (toolbar, q-select) — */
9590
- .q-tiptap__size {
9591
- display: inline-flex;
9592
- align-items: center;
9593
- margin: 0 2px;
9594
- }
9595
- .q-tiptap__font-size {
9596
- width: 100px;
9597
- font-size: 12px;
9598
- }
9599
- .q-tiptap__font-size .q-select__display {
9600
- font-size: 12px;
9601
- }
9602
- .q-tiptap__font-size.q-field--dense .q-select__arrow {
9603
- width: 14px;
9604
- height: 14px;
9605
- }
9606
- /* Popup inline téléporté dans <body> : impossible de le styler depuis le parent — on
9607
- passe par la classe du popup (`inline-options.class`) ; la largeur, elle, vient de
9608
- `inline-options.width` (l'inline posé par positionPopup() prime sur ce CSS). */
9609
- .q-tiptap__font-size-popup {
9610
- min-width: 100px;
9611
- }
9612
- .q-tiptap__font-size-popup .q-select__option-label {
9613
- font-size: 12px;
9614
- }
9615
-
9616
- /* — tableaux : rendu dans le document + popup d'actions — */
9617
- .q-tiptap__editor .ProseMirror .tableWrapper {
9618
- overflow-x: auto;
9619
- }
9620
- .q-tiptap__editor .ProseMirror table {
9621
- width: 100%;
9622
- margin: 0.6em 0;
9623
- border-collapse: collapse;
9624
- table-layout: fixed;
9625
- }
9626
- .q-tiptap__editor .ProseMirror th,
9627
- .q-tiptap__editor .ProseMirror td {
9628
- position: relative;
9629
- min-width: 60px;
9630
- padding: 6px 10px;
9631
- border: 1px solid var(--q-tiptap-border);
9632
- vertical-align: top;
9633
- }
9634
- .q-tiptap__editor .ProseMirror th {
9635
- background: var(--q-tiptap-toolbar-bg);
9636
- font-weight: 600;
9637
- text-align: left;
9638
- }
9639
- .q-tiptap__editor .ProseMirror .selectedCell::after {
9640
- content: "";
9641
- position: absolute;
9642
- inset: 0;
9643
- background: rgb(25 118 210 / 0.14);
9644
- background: color-mix(in srgb, var(--q-tiptap-primary) 14%, transparent);
9645
- pointer-events: none;
9646
- }
9647
- .q-tiptap__editor .ProseMirror .column-resize-handle {
9648
- position: absolute;
9649
- top: 0;
9650
- right: -3px;
9651
- bottom: 0;
9652
- width: 5px;
9653
- background: var(--q-tiptap-primary);
9654
- cursor: col-resize;
9655
- }
9656
- .q-tiptap__editor .ProseMirror td p,
9657
- .q-tiptap__editor .ProseMirror th p {
9658
- margin: 0 0 0.2em;
9659
- }
9660
- .q-tiptap__table-menu {
9661
- position: fixed;
9662
- z-index: 4000;
9663
- display: flex;
9664
- flex-direction: column;
9665
- gap: 2px;
9666
- width: 212px;
9667
- padding: 5px;
9668
- background: var(--q-tiptap-bg);
9669
- border: 1px solid var(--q-tiptap-border);
9670
- border-radius: 10px;
9671
- box-shadow: 0 12px 28px rgb(0 0 0 / 0.18);
9672
- }
9673
- .q-tiptap__table-action {
9674
- display: flex;
9675
- align-items: center;
9676
- width: 100%;
9677
- padding: 6px 10px;
9678
- border: 0;
9679
- border-radius: 7px;
9680
- background: transparent;
9681
- color: var(--q-tiptap-text);
9682
- font-size: 13px;
9683
- text-align: left;
9684
- cursor: pointer;
9685
- }
9686
- .q-tiptap__table-action:hover {
9687
- background: rgb(25 118 210 / 0.12);
9688
- background: color-mix(in srgb, var(--q-tiptap-primary) 14%, transparent);
9689
- color: var(--q-tiptap-primary);
9690
- }
9691
-
9692
- /* ===== QBar ===== */
9693
- .q-bar {
9694
- display: flex;
9695
- align-items: center;
9696
- gap: 10px;
9697
- min-height: 40px;
9698
- padding: 0 12px;
9699
- font-size: 13px;
9700
- line-height: 1.4;
9701
- color: var(--foreground, #1d1d1d);
9702
- background: transparent;
9703
- user-select: none;
9704
- }
9705
- .q-bar--dense {
9706
- gap: 8px;
9707
- min-height: 32px;
9708
- padding: 0 10px;
9709
- font-size: 12px;
9710
- }
9711
- .q-bar--dark {
9712
- color: #ececef;
9713
- background: #1d1d1d;
9714
- }
9715
- .q-bar > * {
9716
- flex-shrink: 0;
9717
- }
9718
- .q-bar__dot {
9719
- width: 12px;
9720
- height: 12px;
9721
- border-radius: 50%;
9722
- }
9723
-
9724
- /* NodeView Vue de la table : wrapper + table + tbody (contentDOM) */
9725
- .q-tiptap__editor .ProseMirror .q-tiptap__table-wrapper {
9726
- position: relative;
9727
- overflow-x: auto;
9728
- margin: 0.6em 0;
9729
- }
9730
- .q-tiptap__editor .ProseMirror .q-tiptap__table-wrapper table {
9731
- margin: 0;
9732
- width: 100%;
9733
- table-layout: fixed;
9734
- }
9735
- /* Poignées de redimensionnement des colonnes (NodeView Vue) */
9736
- .q-tiptap__resize-handle {
9737
- position: absolute;
9738
- top: 0;
9739
- bottom: 0;
9740
- z-index: 2;
9741
- width: 7px;
9742
- margin-left: -3.5px;
9743
- cursor: col-resize;
9744
- touch-action: none;
9745
- }
9746
- .q-tiptap__resize-handle::after {
9747
- content: "";
9748
- position: absolute;
9749
- top: 0;
9750
- bottom: 0;
9751
- left: 2.5px;
9752
- width: 2px;
9753
- border-radius: 2px;
9754
- background: transparent;
9755
- transition: background-color 0.15s ease;
9756
- }
9757
- .q-tiptap__resize-handle:hover::after,
9758
- .q-tiptap__resize-handle:active::after {
9759
- background: var(--q-tiptap-primary);
9760
- }
9761
-
9762
- /* — dialog image / lien (saisie URL) — */
9763
- .q-tiptap__dialog {
9764
- display: flex;
9765
- flex-direction: column;
9766
- gap: 10px;
9767
- min-width: 300px;
9768
- padding: 14px 16px;
9769
- }
9770
-
9771
- /* — task list (NodeView Vue → q-checkbox dnax.ui) — */
9772
- .q-tiptap__editor .ProseMirror ul[data-type="taskList"] {
9773
- padding-left: 0;
9774
- list-style: none;
9775
- }
9776
- .q-tiptap__editor .ProseMirror ul[data-type="taskList"] li[data-node-view-wrapper] {
9777
- display: flex;
9778
- align-items: flex-start;
9779
- gap: 9px;
9780
- list-style: none;
9781
- }
9782
- .q-tiptap__task-check {
9783
- display: inline-flex;
9784
- align-items: center;
9785
- flex-shrink: 0;
9786
- margin-top: 2px;
9787
- cursor: pointer;
9788
- user-select: none;
9789
- }
9790
- .q-tiptap__task-content {
9791
- flex: 1;
9792
- min-width: 0;
9793
- }
9794
- .q-tiptap__task-content--checked {
9795
- color: var(--q-tiptap-muted);
9796
- text-decoration: line-through;
9797
- }
9798
-
9799
- /* ===== QSpreadsheet (tableur type Excel) ===== */
9800
- .q-spreadsheet {
9801
- --q-spreadsheet-border: rgb(0 0 0 / 0.1);
9802
- /* Opaque (cf. QTable : entêtes sticky des colonnes/lignes) */
9803
- --q-spreadsheet-header-bg: #f7f7f7;
9804
- --q-spreadsheet-header-bg: color-mix(in srgb, #000 3%, #fff);
9805
- --q-spreadsheet-row-alt: rgb(0 0 0 / 0.015);
9806
- --q-spreadsheet-range: rgb(25 118 210 / 0.1);
9807
- --q-spreadsheet-range-brd: var(--primary, #1976d2);
9808
-
9809
- position: relative;
9810
- display: flex;
9811
- flex-direction: column;
9812
- width: 100%;
9813
- overflow: hidden;
9814
- border-radius: var(--q-radius, 0);
9815
- background: #fff;
9816
- color: var(--foreground, #1d1d1d);
9817
- font-size: 13px;
9818
- }
9819
- .q-spreadsheet--bordered {
9820
- border: 1px solid var(--q-spreadsheet-border);
9821
- }
9822
- .q-spreadsheet--flat {
9823
- box-shadow: none;
9824
- }
9825
- .q-spreadsheet--disable {
9826
- opacity: 0.6;
9827
- pointer-events: none;
9828
- user-select: none;
9829
- }
9830
- .q-spreadsheet--dark,
9831
- .dark .q-spreadsheet {
9832
- --q-spreadsheet-border: rgb(255 255 255 / 0.12);
9833
- --q-spreadsheet-header-bg: #22262d;
9834
- --q-spreadsheet-header-bg: color-mix(in srgb, #fff 5%, var(--card, #1d1d1d));
9835
- --q-spreadsheet-row-alt: rgb(255 255 255 / 0.02);
9836
- background: var(--card, #1d1d1d);
9837
- color: var(--foreground, #ececef);
9838
- }
9839
-
9840
- /* ─── Barre d'outils ─── */
9841
- .q-spreadsheet__toolbar {
9842
- display: flex;
9843
- align-items: center;
9844
- flex-wrap: wrap;
9845
- gap: 4px;
9846
- padding: 6px 8px;
9847
- border-bottom: 1px solid var(--q-spreadsheet-border);
9848
- background: var(--q-spreadsheet-header-bg);
9849
- }
9850
- .q-spreadsheet__sheet-name {
9851
- display: inline-flex;
9852
- align-items: center;
9853
- gap: 6px;
9854
- margin-right: 6px;
9855
- padding: 3px 10px;
9856
- font-size: 12px;
9857
- font-weight: 600;
9858
- color: var(--foreground, #1d1d1d);
9859
- background: rgb(25 118 210 / 0.08);
9860
- border-radius: 6px;
9861
- white-space: nowrap;
9862
- }
9863
- .q-spreadsheet__tb-group {
9864
- display: inline-flex;
9865
- align-items: center;
9866
- gap: 2px;
9867
- padding: 0 4px;
9868
- border-left: 1px solid var(--q-spreadsheet-border);
9869
- }
9870
- .q-spreadsheet__tool {
9871
- position: relative; /* support du badge « + » des boutons d'ajout */
9872
- display: inline-flex;
9873
- align-items: center;
9874
- justify-content: center;
9875
- width: 28px;
9876
- height: 28px;
9877
- padding: 0;
9878
- border: 0;
9879
- border-radius: 6px;
9880
- background: transparent;
9881
- color: var(--foreground, #1d1d1d);
9882
- cursor: pointer;
9883
- transition: background-color 0.15s ease;
9884
- }
9885
- .q-spreadsheet__tool:hover:not(:disabled) {
9886
- background: rgb(25 118 210 / 0.1);
9887
- color: var(--primary, #1976d2);
9888
- }
9889
- .q-spreadsheet__tool:disabled {
9890
- opacity: 0.35;
9891
- cursor: default;
9892
- }
9893
- .q-spreadsheet__tool .iconify {
9894
- font-size: 17px;
9895
- }
9896
-
9897
- /* Badge « + » des boutons d'ajout de ligne / colonne : le glyphe de fond dit
9898
- l'axe (rows / columns), le + dit l'action d'ajout — sans lui, l'icône seule se
9899
- lit comme « des lignes existantes », pas comme « ajouter une ligne ».
9900
- (Après `.iconify` à spécificité égale → c'est cette taille qui gagne.) */
9901
- .q-spreadsheet__tool .q-spreadsheet__tool-badge {
9902
- position: absolute;
9903
- right: 1px;
9904
- bottom: 1px;
9905
- padding: 1px;
9906
- border-radius: 50%;
9907
- font-size: 11px;
9908
- background: var(--q-spreadsheet-bg, #fff);
9909
- color: var(--primary, #1976d2);
9910
- box-shadow: 0 0 0 1px var(--q-spreadsheet-border);
9911
- }
9912
- .q-spreadsheet--dense .q-spreadsheet__tool {
9913
- width: 24px;
9914
- height: 24px;
9915
- }
9916
-
9917
- /* ─── Viewport / table ─── */
9918
- .q-spreadsheet__viewport {
9919
- position: relative;
9920
- overflow: auto;
9921
- outline: none;
9922
- }
9923
- .q-spreadsheet__table {
9924
- /* PAS de `min-width: 100%` : avec `table-layout: fixed`, un tableau étiré au-delà
9925
- de la somme de ses colonnes RÉPARTIT l'espace libre sur toutes les colonnes
9926
- (proportionnellement) — la gouttière des numéros de ligne (34px) gonflait donc
9927
- jusqu'à ~70px sur une grille étroite, et les largeurs configurées de colonnes
9928
- étaient ignorées. Ici le tableau fait exactement la somme des colonnes ;
9929
- l'espace restant à droite reste vide (comportement tableur). */
9930
- width: max-content;
9931
- border-collapse: separate;
9932
- border-spacing: 0;
9933
- table-layout: fixed;
9934
- font-size: 13px;
9935
- }
9936
-
9937
- /* Grille sans aucune colonne : un seul message → il doit occuper toute la largeur */
9938
- .q-spreadsheet__table--empty {
9939
- min-width: 100%;
9940
- }
9941
-
9942
- /* ─── En-têtes ─── */
9943
- .q-spreadsheet__corner,
9944
- .q-spreadsheet__colhead,
9945
- .q-spreadsheet__rownum {
9946
- position: sticky;
9947
- background-color: var(--q-spreadsheet-header-bg);
9948
- color: var(--foreground, #1d1d1d);
9949
- font-weight: 600;
9950
- user-select: none;
9347
+
9348
+ /* ─── En-têtes ─── */
9349
+ .q-spreadsheet__corner,
9350
+ .q-spreadsheet__colhead,
9351
+ .q-spreadsheet__rownum {
9352
+ position: sticky;
9353
+ background-color: var(--q-spreadsheet-header-bg);
9354
+ color: var(--foreground, #1d1d1d);
9355
+ font-weight: 600;
9356
+ user-select: none;
9951
9357
  }
9952
9358
  .q-spreadsheet__corner {
9953
9359
  top: 0;
@@ -10107,7 +9513,7 @@ a.q-breadcrumbs__el:hover {
10107
9513
  font-size: 12px;
10108
9514
  }
10109
9515
 
10110
- /* ─── Badges (type select + chip) ─── */
9516
+ /* ─── Badges (types select / multiselect + chip) ─── */
10111
9517
  .q-spreadsheet__badge {
10112
9518
  display: inline-block;
10113
9519
  max-width: 100%;
@@ -10122,6 +9528,15 @@ a.q-breadcrumbs__el:hover {
10122
9528
  vertical-align: middle;
10123
9529
  }
10124
9530
 
9531
+ /* Choix multiples : un badge par valeur, qui passent à la ligne dans une cellule étroite */
9532
+ .q-spreadsheet__badges {
9533
+ display: inline-flex;
9534
+ flex-wrap: wrap;
9535
+ gap: 3px;
9536
+ max-width: 100%;
9537
+ vertical-align: middle;
9538
+ }
9539
+
10125
9540
  /* ─── Éditeur overlay ─── */
10126
9541
  .q-spreadsheet__editor {
10127
9542
  position: absolute;
@@ -10186,6 +9601,23 @@ a.q-breadcrumbs__el:hover {
10186
9601
  border: 1px solid var(--q-spreadsheet-border);
10187
9602
  }
10188
9603
 
9604
+ /* Choix multiples : coche de sélection, largeur réservée même décochée (libellés alignés) */
9605
+ .q-spreadsheet__option-check {
9606
+ flex-shrink: 0;
9607
+ display: inline-flex;
9608
+ align-items: center;
9609
+ justify-content: center;
9610
+ width: 14px;
9611
+ height: 14px;
9612
+ font-size: 12px;
9613
+ color: var(--primary, #1976d2);
9614
+ }
9615
+
9616
+ /* Liste un peu plus haute : on coche plusieurs valeurs d'affilée */
9617
+ .q-spreadsheet__editor--multi .q-spreadsheet__options {
9618
+ max-height: 210px;
9619
+ }
9620
+
10189
9621
  /* ─── Barre de formule (fx) ─── */
10190
9622
  .q-spreadsheet__fx {
10191
9623
  display: flex;
@@ -10905,6 +10337,28 @@ a.q-breadcrumbs__el:hover {
10905
10337
  background: rgb(25 118 210 / 0.12);
10906
10338
  color: var(--primary, #1976d2);
10907
10339
  }
10340
+
10341
+ /* Indicateur « modifié » : le document diffère de la référence (rien d'enregistré depuis) */
10342
+ .q-spreadsheet__status-changes {
10343
+ display: inline-flex;
10344
+ align-items: center;
10345
+ gap: 5px;
10346
+ padding: 1px 7px;
10347
+ border-radius: 999px;
10348
+ background: rgb(242 192 55 / 0.16);
10349
+ background: color-mix(in srgb, var(--warning, #f2c037) 18%, transparent);
10350
+ color: var(--warning, #f2c037);
10351
+ font-weight: 600;
10352
+ white-space: nowrap;
10353
+ }
10354
+
10355
+ .q-spreadsheet__changes-dot {
10356
+ flex-shrink: 0;
10357
+ width: 6px;
10358
+ height: 6px;
10359
+ border-radius: 999px;
10360
+ background: currentColor;
10361
+ }
10908
10362
  .q-spreadsheet__status-spacer {
10909
10363
  flex: 1;
10910
10364
  }
@@ -11181,3 +10635,358 @@ a.q-breadcrumbs__el:hover {
11181
10635
  transform 0.225s cubic-bezier(0.4, 0, 0.2, 1),
11182
10636
  opacity 0.25s cubic-bezier(0.4, 0, 0.2, 1);
11183
10637
  }
10638
+
10639
+ /* ===== QMap — carte MapTiler (SDK JS, MapLibre GL) =====
10640
+ Le cadre et les surcouches sont de dnax.ui ; le canvas, les contrôles et les bulles
10641
+ sont rendus par le SDK — d'où les sélecteurs `.maplibregl-*` rethémés ici : aucun
10642
+ blanc/gris fixe du SDK ne doit survivre en mode sombre. */
10643
+ .q-map {
10644
+ position: relative;
10645
+ width: 100%;
10646
+ overflow: hidden;
10647
+ border-radius: var(--q-radius, 0);
10648
+ background: var(--muted, #f5f6f8);
10649
+ }
10650
+
10651
+ .q-map__canvas {
10652
+ width: 100%;
10653
+ height: 100%;
10654
+ }
10655
+
10656
+ /* Surcouche de chargement / d'erreur — le cadre garde sa hauteur (pas de saut de page).
10657
+ `z-index` au-dessus des panes du SDK (2) et de Leaflet (400-800). */
10658
+ .q-map__overlay {
10659
+ position: absolute;
10660
+ inset: 0;
10661
+ z-index: 1000;
10662
+ display: grid;
10663
+ place-items: center;
10664
+ padding: 16px;
10665
+ text-align: center;
10666
+ background: var(--muted, #f5f6f8);
10667
+ color: var(--muted-foreground, #5b6472);
10668
+ }
10669
+
10670
+ .q-map__note {
10671
+ margin: 0;
10672
+ font-size: 13px;
10673
+ line-height: 1.5;
10674
+ }
10675
+
10676
+ /* — Contrôles (zoom, boussole, géolocalisation, plein écran) — */
10677
+ .q-map .maplibregl-ctrl-group {
10678
+ background: var(--card, #fff);
10679
+ border: 1px solid var(--border, rgb(0 0 0 / 0.1));
10680
+ }
10681
+ .q-map .maplibregl-ctrl-group:not(:empty) {
10682
+ box-shadow: 0 1px 3px rgb(0 0 0 / 0.1);
10683
+ }
10684
+ .q-map .maplibregl-ctrl-group button + button {
10685
+ border-top: 1px solid var(--border, rgb(0 0 0 / 0.08));
10686
+ }
10687
+ /* Les icônes du SDK sont des SVG gris foncé en `background-image` : inversées en sombre */
10688
+ .dark .q-map .maplibregl-ctrl button .maplibregl-ctrl-icon {
10689
+ filter: invert(1) brightness(1.7);
10690
+ }
10691
+
10692
+ /* — Bulles (`className: "q-map__popup"` posé par QMap) — */
10693
+ .q-map__popup .maplibregl-popup-content {
10694
+ background: var(--card, #fff);
10695
+ color: var(--foreground, #1d1d1d);
10696
+ border: 1px solid var(--border, rgb(0 0 0 / 0.1));
10697
+ border-radius: var(--q-radius, 8px);
10698
+ padding: 10px 12px;
10699
+ font-size: 13px;
10700
+ line-height: 1.5;
10701
+ box-shadow: 0 6px 24px rgb(0 0 0 / 0.14);
10702
+ }
10703
+ .q-map__popup .maplibregl-popup-close-button {
10704
+ padding: 0 6px;
10705
+ border-radius: var(--q-radius, 8px) 0 0 0;
10706
+ color: var(--muted-foreground, #5b6472);
10707
+ font-size: 16px;
10708
+ }
10709
+ /* La pointe suit la surface de la bulle (8 ancrages du SDK, blancs par défaut) */
10710
+ .q-map__popup.maplibregl-popup-anchor-top .maplibregl-popup-tip,
10711
+ .q-map__popup.maplibregl-popup-anchor-top-left .maplibregl-popup-tip,
10712
+ .q-map__popup.maplibregl-popup-anchor-top-right .maplibregl-popup-tip {
10713
+ border-bottom-color: var(--card, #fff);
10714
+ }
10715
+ .q-map__popup.maplibregl-popup-anchor-bottom .maplibregl-popup-tip,
10716
+ .q-map__popup.maplibregl-popup-anchor-bottom-left .maplibregl-popup-tip,
10717
+ .q-map__popup.maplibregl-popup-anchor-bottom-right .maplibregl-popup-tip {
10718
+ border-top-color: var(--card, #fff);
10719
+ }
10720
+ .q-map__popup.maplibregl-popup-anchor-left .maplibregl-popup-tip {
10721
+ border-right-color: var(--card, #fff);
10722
+ }
10723
+ .q-map__popup.maplibregl-popup-anchor-right .maplibregl-popup-tip {
10724
+ border-left-color: var(--card, #fff);
10725
+ }
10726
+
10727
+ /* ─── Leaflet (provider « leaflet ») ───────────────────────────────────────────
10728
+ Leaflet impose un fond gris, sa police et des contrôles blancs : on les ramène aux
10729
+ tokens du thème, comme pour le SDK MapTiler. */
10730
+ .q-map .leaflet-container {
10731
+ background: var(--muted, #f5f6f8);
10732
+ color: var(--foreground, inherit);
10733
+ font: inherit;
10734
+ font-size: 13px;
10735
+ }
10736
+
10737
+ /* Épingle maison (divIcon SVG) : annule le carré blanc `.leaflet-div-icon` */
10738
+ .q-map .q-map__pin,
10739
+ .q-map .leaflet-div-icon {
10740
+ background: none;
10741
+ border: 0;
10742
+ }
10743
+
10744
+ /* Zoom / contrôles */
10745
+ .q-map .leaflet-bar {
10746
+ border: 1px solid var(--border, rgb(0 0 0 / 0.1));
10747
+ box-shadow: 0 1px 3px rgb(0 0 0 / 0.1);
10748
+ }
10749
+ .q-map .leaflet-bar a,
10750
+ .q-map .leaflet-bar a:hover {
10751
+ background: var(--card, #fff);
10752
+ color: var(--foreground, #1d1d1d);
10753
+ border-bottom-color: var(--border, rgb(0 0 0 / 0.1));
10754
+ }
10755
+ .q-map .leaflet-control-attribution {
10756
+ background: rgb(255 255 255 / 0.75);
10757
+ color: #333;
10758
+ font-size: 11px;
10759
+ }
10760
+
10761
+ /* Bulles (`className: "q-map__popup"` posé par le moteur Leaflet) */
10762
+ .q-map__popup .leaflet-popup-content-wrapper {
10763
+ background: var(--card, #fff);
10764
+ color: var(--foreground, #1d1d1d);
10765
+ border: 1px solid var(--border, rgb(0 0 0 / 0.1));
10766
+ border-radius: var(--q-radius, 8px);
10767
+ box-shadow: 0 6px 24px rgb(0 0 0 / 0.14);
10768
+ }
10769
+ .q-map__popup .leaflet-popup-content {
10770
+ margin: 10px 12px;
10771
+ font-size: 13px;
10772
+ line-height: 1.5;
10773
+ }
10774
+ .q-map__popup .leaflet-popup-tip {
10775
+ background: var(--card, #fff);
10776
+ }
10777
+ .q-map__popup a.leaflet-popup-close-button {
10778
+ color: var(--muted-foreground, #5b6472);
10779
+ }
10780
+
10781
+ /* ===== QInputCurrency — le symbole est un décor, la valeur reste éditable =====
10782
+ Place et espacement du symbole viennent d'`Intl` (« 1 234,56 € » / « $1,234.56 ») : le
10783
+ composant pose `.q-field__currency--before|after` autour de l'input. */
10784
+ .q-field__currency {
10785
+ flex-shrink: 0;
10786
+ font-variant-numeric: tabular-nums;
10787
+ color: var(--muted-foreground, rgb(0 0 0 / 0.55));
10788
+ }
10789
+
10790
+ .q-field__currency--before {
10791
+ margin-right: 3px;
10792
+ }
10793
+
10794
+ .q-field__currency--after {
10795
+ margin-left: 3px;
10796
+ }
10797
+
10798
+ /* Chiffres alignés (montants comparables d'une ligne à l'autre) */
10799
+ .q-input-currency .q-field__native {
10800
+ font-variant-numeric: tabular-nums;
10801
+ }
10802
+
10803
+ /* ===== QQrcode — code QR en un seul chemin SVG =====
10804
+ Le viewBox porte la zone de silence : le SVG se met à l'échelle depuis `size` (ou depuis
10805
+ la largeur CSS) sans jamais flouter les modules (`shape-rendering: crispEdges`). */
10806
+ .q-qrcode {
10807
+ display: block;
10808
+ max-width: 100%;
10809
+ height: auto;
10810
+ }
10811
+
10812
+ /* ===== QNumericKeyboard — pavé numérique à l'écran =====
10813
+ Grille de touches (3 colonnes par défaut, `columns` la change). Les touches sont de vrais
10814
+ boutons : focus visible, état pressé, désactivation — et la hauteur suit `dense`. */
10815
+ .q-numeric-keyboard {
10816
+ --q-nk-columns: 3;
10817
+ --q-nk-key-h: 52px;
10818
+ --q-nk-gap: 8px;
10819
+ --q-nk-dot-size: 12px;
10820
+
10821
+ display: grid;
10822
+ grid-template-columns: repeat(var(--q-nk-columns), minmax(0, 1fr));
10823
+ gap: var(--q-nk-gap);
10824
+ width: 100%;
10825
+ max-width: 360px;
10826
+ user-select: none;
10827
+ -webkit-user-select: none;
10828
+ }
10829
+
10830
+ .q-numeric-keyboard--dense {
10831
+ --q-nk-key-h: 42px;
10832
+ --q-nk-gap: 6px;
10833
+ --q-nk-dot-size: 10px;
10834
+ }
10835
+
10836
+ .q-numeric-keyboard__key {
10837
+ display: flex;
10838
+ align-items: center;
10839
+ justify-content: center;
10840
+ height: var(--q-nk-key-h);
10841
+ border: 1px solid var(--border, rgb(0 0 0 / 0.1));
10842
+ border-radius: var(--q-radius, 10px);
10843
+ background-color: var(--card, #fff);
10844
+ color: var(--foreground, inherit);
10845
+ font: inherit;
10846
+ font-size: 20px;
10847
+ font-weight: 500;
10848
+ cursor: pointer;
10849
+ touch-action: manipulation;
10850
+ -webkit-tap-highlight-color: transparent;
10851
+ transition:
10852
+ background-color 0.15s ease,
10853
+ transform 0.08s ease;
10854
+ }
10855
+
10856
+ .q-numeric-keyboard__key:hover:not(:disabled) {
10857
+ background-color: var(--muted, #f5f6f8);
10858
+ }
10859
+
10860
+ .q-numeric-keyboard__key:active:not(:disabled) {
10861
+ transform: scale(0.97);
10862
+ }
10863
+
10864
+ .q-numeric-keyboard__key:focus-visible {
10865
+ outline: 2px solid var(--primary, #1976d2);
10866
+ outline-offset: 2px;
10867
+ }
10868
+
10869
+ /* Touches d'édition : plus discrètes, mais au même endroit dans la grille */
10870
+ .q-numeric-keyboard__key--backspace,
10871
+ .q-numeric-keyboard__key--clear,
10872
+ .q-numeric-keyboard__key--separator {
10873
+ color: var(--muted-foreground, rgb(0 0 0 / 0.55));
10874
+ font-size: 18px;
10875
+ }
10876
+
10877
+ /* Case libre : occupe sa cellule sans exister visuellement */
10878
+ .q-numeric-keyboard__key--empty {
10879
+ border-color: transparent;
10880
+ background: none;
10881
+ pointer-events: none;
10882
+ }
10883
+
10884
+ .q-numeric-keyboard__key:disabled {
10885
+ opacity: 0.5;
10886
+ cursor: not-allowed;
10887
+ }
10888
+
10889
+ .q-numeric-keyboard--dark .q-numeric-keyboard__key {
10890
+ border-color: rgb(255 255 255 / 0.12);
10891
+ background-color: #1f1f1f;
10892
+ color: #fff;
10893
+ }
10894
+
10895
+ .q-numeric-keyboard--dark .q-numeric-keyboard__key:hover:not(:disabled) {
10896
+ background-color: #2a2a2a;
10897
+ }
10898
+
10899
+ /* Rangée de points : progression d'un code (`show-dots`). Points vides = pastilles creuses,
10900
+ points remplis = couleur primaire, avec un « pop » au remplissage (voir l'index déclaré sur
10901
+ le composant, qui ne rejoue l'animation que pour les points qui viennent de se remplir). */
10902
+ .q-numeric-keyboard__dots {
10903
+ grid-column: 1 / -1;
10904
+ display: flex;
10905
+ flex-wrap: wrap;
10906
+ align-items: center;
10907
+ justify-content: center;
10908
+ gap: 10px;
10909
+ min-height: calc(var(--q-nk-dot-size) + 4px);
10910
+ padding-bottom: 2px;
10911
+ }
10912
+
10913
+ .q-numeric-keyboard__dot {
10914
+ width: var(--q-nk-dot-size);
10915
+ height: var(--q-nk-dot-size);
10916
+ border: 1.5px solid var(--border, rgb(0 0 0 / 0.25));
10917
+ border-radius: 999px;
10918
+ background-color: transparent;
10919
+ transition:
10920
+ background-color 0.18s ease,
10921
+ border-color 0.18s ease;
10922
+ }
10923
+
10924
+ .q-numeric-keyboard__dot--filled {
10925
+ border-color: var(--primary, #1976d2);
10926
+ background-color: var(--primary, #1976d2);
10927
+ }
10928
+
10929
+ @keyframes q-nk-dot-pop {
10930
+ 0% {
10931
+ transform: scale(1);
10932
+ }
10933
+ 45% {
10934
+ transform: scale(1.45);
10935
+ }
10936
+ 100% {
10937
+ transform: scale(1);
10938
+ }
10939
+ }
10940
+
10941
+ .q-numeric-keyboard__dot--pop {
10942
+ animation: q-nk-dot-pop 0.34s ease-out;
10943
+ }
10944
+
10945
+ /* Thème sombre : le contour des points vides doit rester visible sur fond noir */
10946
+ .q-numeric-keyboard--dark .q-numeric-keyboard__dot {
10947
+ border-color: rgb(255 255 255 / 0.3);
10948
+ }
10949
+
10950
+ .q-numeric-keyboard--dark .q-numeric-keyboard__dot--filled {
10951
+ border-color: var(--primary, #1976d2);
10952
+ }
10953
+
10954
+ /* État d'erreur : les points passent en rouge (vides et pleins) + message sous le pavé.
10955
+ Déclaré après les variantes `--dark` : à spécificité égale, l'erreur doit gagner. */
10956
+ .q-numeric-keyboard--error .q-numeric-keyboard__dot {
10957
+ border-color: rgb(193 0 21 / 0.45);
10958
+ border-color: color-mix(in srgb, var(--negative, #c10015) 45%, transparent);
10959
+ }
10960
+
10961
+ .q-numeric-keyboard--error .q-numeric-keyboard__dot--filled {
10962
+ border-color: var(--negative, #c10015);
10963
+ background-color: var(--negative, #c10015);
10964
+ }
10965
+
10966
+ .q-numeric-keyboard__error {
10967
+ grid-column: 1 / -1;
10968
+ margin: 2px 0 0;
10969
+ font-size: 13px;
10970
+ font-weight: 500;
10971
+ line-height: 1.4;
10972
+ color: var(--negative, #c10015);
10973
+ }
10974
+
10975
+ /* « 3 sur 6 » : annonce lecteur d'écran, invisible à l'œil */
10976
+ .q-numeric-keyboard__sr {
10977
+ position: absolute;
10978
+ width: 1px;
10979
+ height: 1px;
10980
+ margin: -1px;
10981
+ padding: 0;
10982
+ overflow: hidden;
10983
+ clip-path: inset(50%);
10984
+ white-space: nowrap;
10985
+ border: 0;
10986
+ }
10987
+
10988
+ @media (prefers-reduced-motion: reduce) {
10989
+ .q-numeric-keyboard__dot--pop {
10990
+ animation: none;
10991
+ }
10992
+ }