@dnax/ui 0.0.48 → 0.0.50

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 (48) hide show
  1. package/components/QBottomSheet.vue +172 -6
  2. package/components/QChart.vue +1 -0
  3. package/components/QDatePicker.vue +1 -1
  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/QSelect.vue +12 -0
  15. package/components/QSpreadsheet.vue +395 -69
  16. package/components/QTime.vue +845 -0
  17. package/components/QTimeline.vue +111 -0
  18. package/components/QTimelineEntry.vue +271 -0
  19. package/index.ts +11 -1
  20. package/lib/bottomSheet.test.ts +121 -0
  21. package/lib/bottomSheet.ts +115 -0
  22. package/lib/color.ts +13 -0
  23. package/lib/currency.test.ts +193 -0
  24. package/lib/currency.ts +301 -0
  25. package/lib/fixedLayout.ts +12 -11
  26. package/lib/icons.ts +3 -20
  27. package/lib/map.test.ts +365 -0
  28. package/lib/map.ts +438 -0
  29. package/lib/mapEngine.ts +158 -0
  30. package/lib/mapLeaflet.ts +142 -0
  31. package/lib/mapMaplibre.ts +106 -0
  32. package/lib/mapMaptiler.ts +93 -0
  33. package/lib/numericKeyboard.test.ts +179 -0
  34. package/lib/numericKeyboard.ts +166 -0
  35. package/lib/pagePadding.test.ts +37 -0
  36. package/lib/pagePadding.ts +32 -0
  37. package/lib/qrcode.test.ts +103 -0
  38. package/lib/qrcode.ts +125 -0
  39. package/lib/spreadsheetChanges.test.ts +230 -0
  40. package/lib/spreadsheetChanges.ts +331 -0
  41. package/package.json +5 -17
  42. package/shims.d.ts +19 -0
  43. package/styles/main.css +689 -884
  44. package/components/QTiptap.vue +0 -996
  45. package/components/internal/QTiptapTableView.vue +0 -123
  46. package/components/internal/QTiptapTaskItemView.vue +0 -49
  47. package/lib/tiptap-table.ts +0 -19
  48. 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` =====
@@ -1619,14 +1663,14 @@ html.dark body {
1619
1663
  height: 55%;
1620
1664
  }
1621
1665
 
1622
- /* ===== Translucide (frosted glass) — partagé header/footer/back-header/bottom-sheet =====
1666
+ /* ===== Translucide (frosted glass) — partagé header/footer/back-header =====
1623
1667
  translucent="70" → 70% opaque + flou derrière (backdrop-filter).
1624
- 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. */
1625
1671
  .q-header--translucent,
1626
1672
  .q-footer--translucent,
1627
- .q-back-header--translucent,
1628
- .q-bottom-sheet__panel--translucent,
1629
- .q-country-picker__sheet--translucent {
1673
+ .q-back-header--translucent {
1630
1674
  background-color: color-mix(
1631
1675
  in srgb,
1632
1676
  var(--q-translucent-bg, #fff) var(--q-translucent-opacity, 70%),
@@ -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;
@@ -9055,903 +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
- /* Hover : jamais sur un bouton actif (même règle que QSidebarMenuButton — sans le
9150
- `:not(...)`, `:hover` (0,2,0) écrasait `.q-tiptap__tool--active` (0,1,0) : le bouton
9151
- actif perdait son fond et sa couleur primaire au survol). */
9152
- .q-tiptap__tool:hover:not(:disabled):not(.q-tiptap__tool--active) {
9153
- background-color: rgb(0 0 0 / 0.06);
9154
- 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);
9155
9294
  }
9156
- .dark .q-tiptap__tool:hover:not(:disabled):not(.q-tiptap__tool--active) {
9157
- background-color: rgb(255 255 255 / 0.08);
9295
+ .q-spreadsheet__tool:disabled {
9296
+ opacity: 0.35;
9297
+ cursor: default;
9158
9298
  }
9159
- .q-tiptap__tool--active {
9160
- background-color: rgb(25 118 210 / 0.14);
9161
- background-color: color-mix(in srgb, var(--q-tiptap-primary) 14%, transparent);
9162
- color: var(--q-tiptap-primary);
9299
+ .q-spreadsheet__tool .iconify {
9300
+ font-size: 17px;
9163
9301
  }
9164
- .q-tiptap__tool:disabled {
9165
- opacity: 0.4;
9166
- 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);
9167
9317
  }
9168
- .q-tiptap--dense .q-tiptap__tool {
9169
- width: 26px;
9170
- height: 26px;
9318
+ .q-spreadsheet--dense .q-spreadsheet__tool {
9319
+ width: 24px;
9320
+ height: 24px;
9171
9321
  }
9172
9322
 
9173
- /* — zone d'édition — */
9174
- .q-tiptap__body {
9175
- position: relative;
9176
- flex: 1;
9177
- min-width: 0;
9178
- }
9179
- .q-tiptap__editor {
9323
+ /* ─── Viewport / table ─── */
9324
+ .q-spreadsheet__viewport {
9180
9325
  position: relative;
9181
- z-index: 1;
9182
- min-height: 180px;
9183
- }
9184
- .q-tiptap__editor > div,
9185
- .q-tiptap__editor .ProseMirror {
9186
- min-height: inherit;
9187
- }
9188
- .q-tiptap__editor .ProseMirror {
9189
- padding: var(--q-tiptap-content-padding, 5%);
9326
+ overflow: auto;
9190
9327
  outline: none;
9191
- font-size: 14px;
9192
- line-height: 1.7;
9193
- color: var(--q-tiptap-text);
9194
- caret-color: var(--q-tiptap-primary);
9195
- word-wrap: break-word;
9196
9328
  }
9197
-
9198
- /* placeholder (visible quand le document est vide) — aligné sur le padding
9199
- par défaut (5% sur les 4 côtés) */
9200
- .q-tiptap__placeholder {
9201
- position: absolute;
9202
- z-index: 0;
9203
- top: 5%;
9204
- left: 5%;
9205
- right: 5%;
9206
- font-style: italic;
9207
- font-size: 14px;
9208
- line-height: 1.7;
9209
- color: var(--q-tiptap-muted);
9210
- pointer-events: none;
9211
- 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;
9212
9341
  }
9213
9342
 
9214
- /* — typographie du contenu édité (ProseMirror) — */
9215
- .q-tiptap__editor .ProseMirror p {
9216
- margin: 0 0 0.6em;
9217
- }
9218
- .q-tiptap__editor .ProseMirror h1,
9219
- .q-tiptap__editor .ProseMirror h2,
9220
- .q-tiptap__editor .ProseMirror h3 {
9221
- margin: 0.8em 0 0.4em;
9222
- color: var(--q-tiptap-text);
9223
- font-weight: 700;
9224
- line-height: 1.3;
9225
- }
9226
- .q-tiptap__editor .ProseMirror h1 {
9227
- font-size: 1.55em;
9228
- }
9229
- .q-tiptap__editor .ProseMirror h2 {
9230
- font-size: 1.3em;
9231
- }
9232
- .q-tiptap__editor .ProseMirror h3 {
9233
- 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%;
9234
9346
  }
9235
- .q-tiptap__editor .ProseMirror > *:first-child {
9236
- margin-top: 0;
9237
- }
9238
- .q-tiptap__editor .ProseMirror ul,
9239
- .q-tiptap__editor .ProseMirror ol {
9240
- margin: 0 0 0.8em;
9241
- /* listes décalées d'un retrait (margin-left) ; petit padding pour les
9242
- marqueurs (puces / numéros) */
9243
- margin-left: 1.5em;
9244
- padding-left: 0.4em;
9245
- }
9246
- /* listes imbriquées : retrait réduit pour rester lisible */
9247
- .q-tiptap__editor .ProseMirror ul ul,
9248
- .q-tiptap__editor .ProseMirror ul ol,
9249
- .q-tiptap__editor .ProseMirror ol ul,
9250
- .q-tiptap__editor .ProseMirror ol ol {
9251
- margin-left: 0.75em;
9252
- }
9253
- /* Tailwind preflight met ul/ol { list-style: none } → restaurer les marqueurs */
9254
- .q-tiptap__editor .ProseMirror ul {
9255
- list-style-type: disc;
9256
- }
9257
- .q-tiptap__editor .ProseMirror ol {
9258
- list-style-type: decimal;
9259
- }
9260
- .q-tiptap__editor .ProseMirror ul ul {
9261
- list-style-type: circle;
9262
- }
9263
- .q-tiptap__editor .ProseMirror ul ul ul {
9264
- list-style-type: square;
9265
- }
9266
- .q-tiptap__editor .ProseMirror li {
9267
- margin: 0.15em 0;
9268
- }
9269
- .q-tiptap__editor .ProseMirror li p {
9270
- margin: 0;
9271
- }
9272
- .q-tiptap__editor .ProseMirror blockquote {
9273
- margin: 0.6em 0;
9274
- padding: 6px 12px;
9275
- border-left: 3px solid var(--q-tiptap-primary);
9276
- border-radius: 0 8px 8px 0;
9277
- background: var(--q-tiptap-quote-bg);
9278
- }
9279
- .q-tiptap__editor .ProseMirror blockquote p {
9280
- margin: 0;
9281
- }
9282
- .q-tiptap__editor .ProseMirror pre {
9283
- margin: 0.6em 0;
9284
- padding: 10px 12px;
9285
- overflow-x: auto;
9286
- border: 1px solid var(--q-tiptap-border);
9287
- border-radius: 8px;
9288
- background: var(--q-tiptap-code-bg);
9289
- font-size: 0.92em;
9290
- }
9291
- .q-tiptap__editor .ProseMirror pre code {
9292
- background: none;
9293
- padding: 0;
9294
- font-size: 1em;
9295
- }
9296
- .q-tiptap__editor .ProseMirror code {
9297
- padding: 0.1em 0.35em;
9298
- border-radius: 4px;
9299
- background: var(--q-tiptap-code-bg);
9300
- font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
9301
- font-size: 0.92em;
9302
- }
9303
- .q-tiptap__editor .ProseMirror a {
9304
- color: var(--q-tiptap-primary);
9305
- text-decoration: underline;
9306
- cursor: pointer;
9307
- }
9308
- .q-tiptap__editor .ProseMirror hr {
9309
- margin: 0.8em 0;
9310
- border: none;
9311
- border-top: 1px solid var(--q-tiptap-border);
9312
- }
9313
- .q-tiptap__editor .ProseMirror img {
9314
- display: block;
9315
- max-width: 100%;
9316
- height: auto;
9317
- margin: 0.6em auto;
9318
- border-radius: 8px;
9319
- box-shadow: 0 0 0 1px var(--q-tiptap-border);
9320
- }
9321
- .q-tiptap__editor .ProseMirror .ProseMirror-selectednode {
9322
- outline: 2px solid var(--q-tiptap-primary);
9323
- border-radius: 4px;
9324
- }
9325
-
9326
- /* — couleur de texte (toolbar + palette popover) — */
9327
- .q-tiptap__tool--color {
9328
- position: relative;
9329
- }
9330
- .q-tiptap__color-bar {
9331
- position: absolute;
9332
- right: 7px;
9333
- bottom: 4px;
9334
- left: 7px;
9335
- height: 3px;
9336
- border-radius: 2px;
9337
- pointer-events: none;
9338
- }
9339
- .q-tiptap__tool--disabled {
9340
- opacity: 0.4;
9341
- cursor: not-allowed;
9342
- }
9343
- .q-tiptap__tool--color:focus-visible {
9344
- box-shadow: inset 0 0 0 2px var(--q-tiptap-primary);
9345
- }
9346
- .q-tiptap__palette {
9347
- position: fixed;
9348
- z-index: 4000;
9349
- width: 224px;
9350
- padding: 10px;
9351
- background: var(--q-tiptap-bg);
9352
- border: 1px solid var(--q-tiptap-border);
9353
- border-radius: 12px;
9354
- box-shadow: 0 14px 34px rgb(0 0 0 / 0.18);
9355
- }
9356
- .q-tiptap__palette-title {
9357
- margin: 0 2px 8px;
9358
- font-size: 11px;
9359
- font-weight: 600;
9360
- text-transform: uppercase;
9361
- letter-spacing: 0.05em;
9362
- color: var(--q-tiptap-muted);
9363
- }
9364
- .q-tiptap__palette-swatches {
9365
- display: grid;
9366
- grid-template-columns: repeat(7, 1fr);
9367
- gap: 6px;
9368
- }
9369
- .q-tiptap__swatch {
9370
- position: relative;
9371
- width: 100%;
9372
- aspect-ratio: 1;
9373
- padding: 0;
9374
- border: 1px solid var(--q-tiptap-border);
9375
- border-radius: 6px;
9376
- background: transparent;
9377
- cursor: pointer;
9378
- transition: transform 0.1s ease;
9379
- }
9380
- .q-tiptap__swatch:hover {
9381
- transform: scale(1.12);
9382
- }
9383
- .q-tiptap__swatch--active {
9384
- box-shadow:
9385
- 0 0 0 2px var(--q-tiptap-bg),
9386
- 0 0 0 4px var(--q-tiptap-primary);
9387
- }
9388
- .q-tiptap__swatch--none {
9389
- background: #fff;
9390
- background-image: linear-gradient(
9391
- to top right,
9392
- transparent calc(50% - 1px),
9393
- #e5484d calc(50% - 1px),
9394
- #e5484d calc(50% + 1px),
9395
- transparent calc(50% + 1px)
9396
- );
9397
- }
9398
- .q-tiptap__palette-custom {
9399
- display: flex;
9400
- align-items: center;
9401
- gap: 8px;
9402
- margin-top: 10px;
9403
- padding-top: 10px;
9404
- border-top: 1px solid var(--q-tiptap-border);
9405
- font-size: 12px;
9406
- color: var(--q-tiptap-muted);
9407
- cursor: pointer;
9408
- }
9409
- .q-tiptap__palette-custom input[type="color"] {
9410
- width: 26px;
9411
- height: 26px;
9412
- padding: 0;
9413
- border: 1px solid var(--q-tiptap-border);
9414
- border-radius: 6px;
9415
- background: none;
9416
- cursor: pointer;
9417
- }
9418
- .q-tiptap__palette-custom input[type="color"]::-webkit-color-swatch-wrapper {
9419
- padding: 2px;
9420
- }
9421
- .q-tiptap__palette-custom input[type="color"]::-webkit-color-swatch {
9422
- border: none;
9423
- border-radius: 4px;
9424
- }
9425
-
9426
- /* — mentions (@) : puce dans le document + popup de suggestion — */
9427
- .q-tiptap__editor .ProseMirror span[data-type="mention"] {
9428
- display: inline-block;
9429
- padding: 0 5px;
9430
- margin: 0 1px;
9431
- border-radius: 6px;
9432
- background: rgb(25 118 210 / 0.12);
9433
- background: color-mix(in srgb, var(--q-tiptap-primary) 14%, transparent);
9434
- color: var(--q-tiptap-primary);
9435
- font-weight: 500;
9436
- white-space: nowrap;
9437
- }
9438
- .q-tiptap__mention-list {
9439
- display: flex;
9440
- flex-direction: column;
9441
- gap: 2px;
9442
- width: 220px;
9443
- max-height: 240px;
9444
- overflow-y: auto;
9445
- padding: 4px;
9446
- background: var(--q-tiptap-bg);
9447
- border: 1px solid var(--q-tiptap-border);
9448
- border-radius: 10px;
9449
- box-shadow: 0 12px 28px rgb(0 0 0 / 0.18);
9450
- }
9451
- .q-tiptap__mention-item {
9452
- display: flex;
9453
- align-items: center;
9454
- width: 100%;
9455
- padding: 6px 10px;
9456
- border: 0;
9457
- border-radius: 7px;
9458
- background: transparent;
9459
- color: var(--q-tiptap-text);
9460
- font-size: 13px;
9461
- text-align: left;
9462
- cursor: pointer;
9463
- white-space: nowrap;
9464
- overflow: hidden;
9465
- text-overflow: ellipsis;
9466
- }
9467
- .q-tiptap__mention-item:hover,
9468
- .q-tiptap__mention-item--active {
9469
- background: rgb(25 118 210 / 0.12);
9470
- background: color-mix(in srgb, var(--q-tiptap-primary) 14%, transparent);
9471
- color: var(--q-tiptap-primary);
9472
- }
9473
- .q-tiptap__mention-empty {
9474
- padding: 8px 10px;
9475
- font-size: 13px;
9476
- color: var(--q-tiptap-muted);
9477
- }
9478
-
9479
- /* — bubble menu : barre contextuelle de formatage (sélection) — */
9480
- .q-tiptap__bubble {
9481
- display: flex;
9482
- align-items: center;
9483
- gap: 2px;
9484
- width: max-content;
9485
- padding: 4px;
9486
- background: var(--q-tiptap-bg);
9487
- border: 1px solid var(--q-tiptap-border);
9488
- border-radius: 12px;
9489
- box-shadow: 0 10px 28px rgb(0 0 0 / 0.16);
9490
- }
9491
- .q-tiptap__bubble-btn {
9492
- display: inline-flex;
9493
- align-items: center;
9494
- justify-content: center;
9495
- width: 28px;
9496
- height: 28px;
9497
- padding: 0;
9498
- border: 0;
9499
- border-radius: 8px;
9500
- background: transparent;
9501
- color: var(--q-tiptap-muted);
9502
- cursor: pointer;
9503
- transition:
9504
- background-color 0.15s ease,
9505
- color 0.15s ease;
9506
- }
9507
- .q-tiptap__bubble-btn svg {
9508
- width: 15px;
9509
- height: 15px;
9510
- }
9511
- /* Idem : un bouton de bulle actif garde son apparence au survol */
9512
- .q-tiptap__bubble-btn:hover:not(:disabled):not(.q-tiptap__bubble-btn--active) {
9513
- background-color: rgb(0 0 0 / 0.06);
9514
- color: var(--q-tiptap-text);
9515
- }
9516
- .dark .q-tiptap__bubble-btn:hover:not(:disabled):not(.q-tiptap__bubble-btn--active) {
9517
- background-color: rgb(255 255 255 / 0.08);
9518
- }
9519
- .q-tiptap__bubble-btn--active {
9520
- background-color: rgb(25 118 210 / 0.14);
9521
- background-color: color-mix(in srgb, var(--q-tiptap-primary) 14%, transparent);
9522
- color: var(--q-tiptap-primary);
9523
- }
9524
- .q-tiptap__bubble-btn:disabled {
9525
- opacity: 0.4;
9526
- cursor: not-allowed;
9527
- }
9528
- .q-tiptap__bubble-sep {
9529
- width: 1px;
9530
- height: 16px;
9531
- margin: 0 3px;
9532
- background: var(--q-tiptap-border);
9533
- }
9534
- .q-tiptap__bubble .q-tiptap__color-bar {
9535
- right: 6px;
9536
- bottom: 3px;
9537
- left: 6px;
9538
- }
9539
-
9540
- /* — drag handle : poignée de déplacement des blocs — */
9541
- .q-tiptap__drag-handle {
9542
- display: inline-flex;
9543
- align-items: center;
9544
- justify-content: center;
9545
- width: 20px;
9546
- height: 20px;
9547
- /* espace minimum entre l'icône et l'élément dragué (≥ 5% de la largeur de
9548
- l'éditeur, relatif au conteneur positionné .q-tiptap__body) */
9549
- margin-right: 5%;
9550
- color: var(--q-tiptap-muted);
9551
- cursor: grab;
9552
- border-radius: 6px;
9553
- transition: color 0.15s ease, background-color 0.15s ease;
9554
- }
9555
- .q-tiptap__drag-handle:hover {
9556
- color: var(--q-tiptap-primary);
9557
- background: rgb(25 118 210 / 0.12);
9558
- background: color-mix(in srgb, var(--q-tiptap-primary) 14%, transparent);
9559
- }
9560
- .q-tiptap__drag-handle:active {
9561
- cursor: grabbing;
9562
- }
9563
- .q-tiptap__drag-handle svg {
9564
- width: 16px;
9565
- height: 16px;
9566
- }
9567
-
9568
- /* Les popups (mentions, palette, bubble) sont montés HORS de .q-tiptap
9569
- (Teleport / body via props.mount) → ils n'héritent PAS de --q-tiptap-*.
9570
- On leur redéfinit leurs variables localement : fond blanc en light
9571
- (--card), sombre via html.dark. */
9572
- .q-tiptap__mention-list,
9573
- .q-tiptap__palette,
9574
- .q-tiptap__bubble,
9575
- .q-tiptap__table-menu {
9576
- --q-tiptap-bg: var(--card, #ffffff);
9577
- --q-tiptap-border: var(--border, rgb(0 0 0 / 0.12));
9578
- --q-tiptap-text: var(--foreground, #1d1d1d);
9579
- --q-tiptap-muted: #8b93a1;
9580
- --q-tiptap-primary: var(--primary, #1976d2);
9581
- }
9582
- .dark .q-tiptap__mention-list,
9583
- .dark .q-tiptap__palette,
9584
- .dark .q-tiptap__bubble,
9585
- .dark .q-tiptap__table-menu {
9586
- --q-tiptap-bg: #1e1e22;
9587
- --q-tiptap-border: rgb(255 255 255 / 0.12);
9588
- --q-tiptap-text: #ececef;
9589
- --q-tiptap-muted: #9aa2b1;
9590
- --q-tiptap-primary: var(--primary, #1976d2);
9591
- }
9592
-
9593
- /* — sélecteur de taille de police (toolbar, q-select) — */
9594
- .q-tiptap__size {
9595
- display: inline-flex;
9596
- align-items: center;
9597
- margin: 0 2px;
9598
- }
9599
- .q-tiptap__font-size {
9600
- width: 100px;
9601
- font-size: 12px;
9602
- }
9603
- .q-tiptap__font-size .q-select__display {
9604
- font-size: 12px;
9605
- }
9606
- .q-tiptap__font-size.q-field--dense .q-select__arrow {
9607
- width: 14px;
9608
- height: 14px;
9609
- }
9610
- /* Popup inline téléporté dans <body> : impossible de le styler depuis le parent — on
9611
- passe par la classe du popup (`inline-options.class`) ; la largeur, elle, vient de
9612
- `inline-options.width` (l'inline posé par positionPopup() prime sur ce CSS). */
9613
- .q-tiptap__font-size-popup {
9614
- min-width: 100px;
9615
- }
9616
- .q-tiptap__font-size-popup .q-select__option-label {
9617
- font-size: 12px;
9618
- }
9619
-
9620
- /* — tableaux : rendu dans le document + popup d'actions — */
9621
- .q-tiptap__editor .ProseMirror .tableWrapper {
9622
- overflow-x: auto;
9623
- }
9624
- .q-tiptap__editor .ProseMirror table {
9625
- width: 100%;
9626
- margin: 0.6em 0;
9627
- border-collapse: collapse;
9628
- table-layout: fixed;
9629
- }
9630
- .q-tiptap__editor .ProseMirror th,
9631
- .q-tiptap__editor .ProseMirror td {
9632
- position: relative;
9633
- min-width: 60px;
9634
- padding: 6px 10px;
9635
- border: 1px solid var(--q-tiptap-border);
9636
- vertical-align: top;
9637
- }
9638
- .q-tiptap__editor .ProseMirror th {
9639
- background: var(--q-tiptap-toolbar-bg);
9640
- font-weight: 600;
9641
- text-align: left;
9642
- }
9643
- .q-tiptap__editor .ProseMirror .selectedCell::after {
9644
- content: "";
9645
- position: absolute;
9646
- inset: 0;
9647
- background: rgb(25 118 210 / 0.14);
9648
- background: color-mix(in srgb, var(--q-tiptap-primary) 14%, transparent);
9649
- pointer-events: none;
9650
- }
9651
- .q-tiptap__editor .ProseMirror .column-resize-handle {
9652
- position: absolute;
9653
- top: 0;
9654
- right: -3px;
9655
- bottom: 0;
9656
- width: 5px;
9657
- background: var(--q-tiptap-primary);
9658
- cursor: col-resize;
9659
- }
9660
- .q-tiptap__editor .ProseMirror td p,
9661
- .q-tiptap__editor .ProseMirror th p {
9662
- margin: 0 0 0.2em;
9663
- }
9664
- .q-tiptap__table-menu {
9665
- position: fixed;
9666
- z-index: 4000;
9667
- display: flex;
9668
- flex-direction: column;
9669
- gap: 2px;
9670
- width: 212px;
9671
- padding: 5px;
9672
- background: var(--q-tiptap-bg);
9673
- border: 1px solid var(--q-tiptap-border);
9674
- border-radius: 10px;
9675
- box-shadow: 0 12px 28px rgb(0 0 0 / 0.18);
9676
- }
9677
- .q-tiptap__table-action {
9678
- display: flex;
9679
- align-items: center;
9680
- width: 100%;
9681
- padding: 6px 10px;
9682
- border: 0;
9683
- border-radius: 7px;
9684
- background: transparent;
9685
- color: var(--q-tiptap-text);
9686
- font-size: 13px;
9687
- text-align: left;
9688
- cursor: pointer;
9689
- }
9690
- .q-tiptap__table-action:hover {
9691
- background: rgb(25 118 210 / 0.12);
9692
- background: color-mix(in srgb, var(--q-tiptap-primary) 14%, transparent);
9693
- color: var(--q-tiptap-primary);
9694
- }
9695
-
9696
- /* ===== QBar ===== */
9697
- .q-bar {
9698
- display: flex;
9699
- align-items: center;
9700
- gap: 10px;
9701
- min-height: 40px;
9702
- padding: 0 12px;
9703
- font-size: 13px;
9704
- line-height: 1.4;
9705
- color: var(--foreground, #1d1d1d);
9706
- background: transparent;
9707
- user-select: none;
9708
- }
9709
- .q-bar--dense {
9710
- gap: 8px;
9711
- min-height: 32px;
9712
- padding: 0 10px;
9713
- font-size: 12px;
9714
- }
9715
- .q-bar--dark {
9716
- color: #ececef;
9717
- background: #1d1d1d;
9718
- }
9719
- .q-bar > * {
9720
- flex-shrink: 0;
9721
- }
9722
- .q-bar__dot {
9723
- width: 12px;
9724
- height: 12px;
9725
- border-radius: 50%;
9726
- }
9727
-
9728
- /* NodeView Vue de la table : wrapper + table + tbody (contentDOM) */
9729
- .q-tiptap__editor .ProseMirror .q-tiptap__table-wrapper {
9730
- position: relative;
9731
- overflow-x: auto;
9732
- margin: 0.6em 0;
9733
- }
9734
- .q-tiptap__editor .ProseMirror .q-tiptap__table-wrapper table {
9735
- margin: 0;
9736
- width: 100%;
9737
- table-layout: fixed;
9738
- }
9739
- /* Poignées de redimensionnement des colonnes (NodeView Vue) */
9740
- .q-tiptap__resize-handle {
9741
- position: absolute;
9742
- top: 0;
9743
- bottom: 0;
9744
- z-index: 2;
9745
- width: 7px;
9746
- margin-left: -3.5px;
9747
- cursor: col-resize;
9748
- touch-action: none;
9749
- }
9750
- .q-tiptap__resize-handle::after {
9751
- content: "";
9752
- position: absolute;
9753
- top: 0;
9754
- bottom: 0;
9755
- left: 2.5px;
9756
- width: 2px;
9757
- border-radius: 2px;
9758
- background: transparent;
9759
- transition: background-color 0.15s ease;
9760
- }
9761
- .q-tiptap__resize-handle:hover::after,
9762
- .q-tiptap__resize-handle:active::after {
9763
- background: var(--q-tiptap-primary);
9764
- }
9765
-
9766
- /* — dialog image / lien (saisie URL) — */
9767
- .q-tiptap__dialog {
9768
- display: flex;
9769
- flex-direction: column;
9770
- gap: 10px;
9771
- min-width: 300px;
9772
- padding: 14px 16px;
9773
- }
9774
-
9775
- /* — task list (NodeView Vue → q-checkbox dnax.ui) — */
9776
- .q-tiptap__editor .ProseMirror ul[data-type="taskList"] {
9777
- padding-left: 0;
9778
- list-style: none;
9779
- }
9780
- .q-tiptap__editor .ProseMirror ul[data-type="taskList"] li[data-node-view-wrapper] {
9781
- display: flex;
9782
- align-items: flex-start;
9783
- gap: 9px;
9784
- list-style: none;
9785
- }
9786
- .q-tiptap__task-check {
9787
- display: inline-flex;
9788
- align-items: center;
9789
- flex-shrink: 0;
9790
- margin-top: 2px;
9791
- cursor: pointer;
9792
- user-select: none;
9793
- }
9794
- .q-tiptap__task-content {
9795
- flex: 1;
9796
- min-width: 0;
9797
- }
9798
- .q-tiptap__task-content--checked {
9799
- color: var(--q-tiptap-muted);
9800
- text-decoration: line-through;
9801
- }
9802
-
9803
- /* ===== QSpreadsheet (tableur type Excel) ===== */
9804
- .q-spreadsheet {
9805
- --q-spreadsheet-border: rgb(0 0 0 / 0.1);
9806
- /* Opaque (cf. QTable : entêtes sticky des colonnes/lignes) */
9807
- --q-spreadsheet-header-bg: #f7f7f7;
9808
- --q-spreadsheet-header-bg: color-mix(in srgb, #000 3%, #fff);
9809
- --q-spreadsheet-row-alt: rgb(0 0 0 / 0.015);
9810
- --q-spreadsheet-range: rgb(25 118 210 / 0.1);
9811
- --q-spreadsheet-range-brd: var(--primary, #1976d2);
9812
-
9813
- position: relative;
9814
- display: flex;
9815
- flex-direction: column;
9816
- width: 100%;
9817
- overflow: hidden;
9818
- border-radius: var(--q-radius, 0);
9819
- background: #fff;
9820
- color: var(--foreground, #1d1d1d);
9821
- font-size: 13px;
9822
- }
9823
- .q-spreadsheet--bordered {
9824
- border: 1px solid var(--q-spreadsheet-border);
9825
- }
9826
- .q-spreadsheet--flat {
9827
- box-shadow: none;
9828
- }
9829
- .q-spreadsheet--disable {
9830
- opacity: 0.6;
9831
- pointer-events: none;
9832
- user-select: none;
9833
- }
9834
- .q-spreadsheet--dark,
9835
- .dark .q-spreadsheet {
9836
- --q-spreadsheet-border: rgb(255 255 255 / 0.12);
9837
- --q-spreadsheet-header-bg: #22262d;
9838
- --q-spreadsheet-header-bg: color-mix(in srgb, #fff 5%, var(--card, #1d1d1d));
9839
- --q-spreadsheet-row-alt: rgb(255 255 255 / 0.02);
9840
- background: var(--card, #1d1d1d);
9841
- color: var(--foreground, #ececef);
9842
- }
9843
-
9844
- /* ─── Barre d'outils ─── */
9845
- .q-spreadsheet__toolbar {
9846
- display: flex;
9847
- align-items: center;
9848
- flex-wrap: wrap;
9849
- gap: 4px;
9850
- padding: 6px 8px;
9851
- border-bottom: 1px solid var(--q-spreadsheet-border);
9852
- background: var(--q-spreadsheet-header-bg);
9853
- }
9854
- .q-spreadsheet__sheet-name {
9855
- display: inline-flex;
9856
- align-items: center;
9857
- gap: 6px;
9858
- margin-right: 6px;
9859
- padding: 3px 10px;
9860
- font-size: 12px;
9861
- font-weight: 600;
9862
- color: var(--foreground, #1d1d1d);
9863
- background: rgb(25 118 210 / 0.08);
9864
- border-radius: 6px;
9865
- white-space: nowrap;
9866
- }
9867
- .q-spreadsheet__tb-group {
9868
- display: inline-flex;
9869
- align-items: center;
9870
- gap: 2px;
9871
- padding: 0 4px;
9872
- border-left: 1px solid var(--q-spreadsheet-border);
9873
- }
9874
- .q-spreadsheet__tool {
9875
- position: relative; /* support du badge « + » des boutons d'ajout */
9876
- display: inline-flex;
9877
- align-items: center;
9878
- justify-content: center;
9879
- width: 28px;
9880
- height: 28px;
9881
- padding: 0;
9882
- border: 0;
9883
- border-radius: 6px;
9884
- background: transparent;
9885
- color: var(--foreground, #1d1d1d);
9886
- cursor: pointer;
9887
- transition: background-color 0.15s ease;
9888
- }
9889
- .q-spreadsheet__tool:hover:not(:disabled) {
9890
- background: rgb(25 118 210 / 0.1);
9891
- color: var(--primary, #1976d2);
9892
- }
9893
- .q-spreadsheet__tool:disabled {
9894
- opacity: 0.35;
9895
- cursor: default;
9896
- }
9897
- .q-spreadsheet__tool .iconify {
9898
- font-size: 17px;
9899
- }
9900
-
9901
- /* Badge « + » des boutons d'ajout de ligne / colonne : le glyphe de fond dit
9902
- l'axe (rows / columns), le + dit l'action d'ajout — sans lui, l'icône seule se
9903
- lit comme « des lignes existantes », pas comme « ajouter une ligne ».
9904
- (Après `.iconify` à spécificité égale → c'est cette taille qui gagne.) */
9905
- .q-spreadsheet__tool .q-spreadsheet__tool-badge {
9906
- position: absolute;
9907
- right: 1px;
9908
- bottom: 1px;
9909
- padding: 1px;
9910
- border-radius: 50%;
9911
- font-size: 11px;
9912
- background: var(--q-spreadsheet-bg, #fff);
9913
- color: var(--primary, #1976d2);
9914
- box-shadow: 0 0 0 1px var(--q-spreadsheet-border);
9915
- }
9916
- .q-spreadsheet--dense .q-spreadsheet__tool {
9917
- width: 24px;
9918
- height: 24px;
9919
- }
9920
-
9921
- /* ─── Viewport / table ─── */
9922
- .q-spreadsheet__viewport {
9923
- position: relative;
9924
- overflow: auto;
9925
- outline: none;
9926
- }
9927
- .q-spreadsheet__table {
9928
- /* PAS de `min-width: 100%` : avec `table-layout: fixed`, un tableau étiré au-delà
9929
- de la somme de ses colonnes RÉPARTIT l'espace libre sur toutes les colonnes
9930
- (proportionnellement) — la gouttière des numéros de ligne (34px) gonflait donc
9931
- jusqu'à ~70px sur une grille étroite, et les largeurs configurées de colonnes
9932
- étaient ignorées. Ici le tableau fait exactement la somme des colonnes ;
9933
- l'espace restant à droite reste vide (comportement tableur). */
9934
- width: max-content;
9935
- border-collapse: separate;
9936
- border-spacing: 0;
9937
- table-layout: fixed;
9938
- font-size: 13px;
9939
- }
9940
-
9941
- /* Grille sans aucune colonne : un seul message → il doit occuper toute la largeur */
9942
- .q-spreadsheet__table--empty {
9943
- min-width: 100%;
9944
- }
9945
-
9946
- /* ─── En-têtes ─── */
9947
- .q-spreadsheet__corner,
9948
- .q-spreadsheet__colhead,
9949
- .q-spreadsheet__rownum {
9950
- position: sticky;
9951
- background-color: var(--q-spreadsheet-header-bg);
9952
- color: var(--foreground, #1d1d1d);
9953
- font-weight: 600;
9954
- 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;
9955
9357
  }
9956
9358
  .q-spreadsheet__corner {
9957
9359
  top: 0;
@@ -10111,7 +9513,7 @@ a.q-breadcrumbs__el:hover {
10111
9513
  font-size: 12px;
10112
9514
  }
10113
9515
 
10114
- /* ─── Badges (type select + chip) ─── */
9516
+ /* ─── Badges (types select / multiselect + chip) ─── */
10115
9517
  .q-spreadsheet__badge {
10116
9518
  display: inline-block;
10117
9519
  max-width: 100%;
@@ -10126,6 +9528,15 @@ a.q-breadcrumbs__el:hover {
10126
9528
  vertical-align: middle;
10127
9529
  }
10128
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
+
10129
9540
  /* ─── Éditeur overlay ─── */
10130
9541
  .q-spreadsheet__editor {
10131
9542
  position: absolute;
@@ -10190,6 +9601,23 @@ a.q-breadcrumbs__el:hover {
10190
9601
  border: 1px solid var(--q-spreadsheet-border);
10191
9602
  }
10192
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
+
10193
9621
  /* ─── Barre de formule (fx) ─── */
10194
9622
  .q-spreadsheet__fx {
10195
9623
  display: flex;
@@ -10909,6 +10337,28 @@ a.q-breadcrumbs__el:hover {
10909
10337
  background: rgb(25 118 210 / 0.12);
10910
10338
  color: var(--primary, #1976d2);
10911
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
+ }
10912
10362
  .q-spreadsheet__status-spacer {
10913
10363
  flex: 1;
10914
10364
  }
@@ -11185,3 +10635,358 @@ a.q-breadcrumbs__el:hover {
11185
10635
  transform 0.225s cubic-bezier(0.4, 0, 0.2, 1),
11186
10636
  opacity 0.25s cubic-bezier(0.4, 0, 0.2, 1);
11187
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
+ }