@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.
- package/components/QBottomSheet.vue +172 -6
- package/components/QBtn.vue +3 -4
- package/components/QContainer.vue +2 -2
- package/components/QImagePicker.vue +28 -1
- package/components/QInputChat.vue +859 -0
- package/components/QInputCurrency.vue +319 -0
- package/components/QItemLabel.vue +99 -0
- package/components/QMap.vue +410 -0
- package/components/QNumericKeyboard.vue +304 -0
- package/components/QPage.vue +26 -6
- package/components/QPageContainer.vue +32 -0
- package/components/QPopupProxy.vue +378 -0
- package/components/QQrcode.vue +132 -0
- package/components/QSpreadsheet.vue +395 -69
- package/components/QTime.vue +845 -0
- package/components/QTimeline.vue +111 -0
- package/components/QTimelineEntry.vue +271 -0
- package/index.ts +11 -1
- package/lib/bottomSheet.test.ts +121 -0
- package/lib/bottomSheet.ts +115 -0
- package/lib/color.ts +13 -0
- package/lib/currency.test.ts +193 -0
- package/lib/currency.ts +301 -0
- package/lib/fixedLayout.ts +12 -11
- package/lib/icons.ts +3 -20
- package/lib/map.test.ts +365 -0
- package/lib/map.ts +438 -0
- package/lib/mapEngine.ts +158 -0
- package/lib/mapLeaflet.ts +142 -0
- package/lib/mapMaplibre.ts +106 -0
- package/lib/mapMaptiler.ts +93 -0
- package/lib/numericKeyboard.test.ts +179 -0
- package/lib/numericKeyboard.ts +166 -0
- package/lib/pagePadding.test.ts +37 -0
- package/lib/pagePadding.ts +32 -0
- package/lib/qrcode.test.ts +103 -0
- package/lib/qrcode.ts +125 -0
- package/lib/spreadsheetChanges.test.ts +230 -0
- package/lib/spreadsheetChanges.ts +331 -0
- package/package.json +5 -17
- package/shims.d.ts +19 -0
- package/styles/main.css +702 -893
- package/components/QTiptap.vue +0 -996
- package/components/internal/QTiptapTableView.vue +0 -123
- package/components/internal/QTiptapTaskItemView.vue +0 -49
- package/lib/tiptap-table.ts +0 -19
- package/lib/tiptap-task-list.ts +0 -16
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// QInputCurrency — champ montant : la saisie est formatée à la volée (groupement des
|
|
3
|
+
// milliers, décimales et séparateur de la locale) et le `v-model` porte un **nombre**
|
|
4
|
+
// (`number | null`), jamais la chaîne affichée.
|
|
5
|
+
//
|
|
6
|
+
// <q-input-currency v-model="price" label="Prix" currency="EUR" outlined dense />
|
|
7
|
+
// <q-input-currency v-model="amount" currency="USD" locale="en-US" :min="0" />
|
|
8
|
+
//
|
|
9
|
+
// Le symbole de la devise est un **décor** : sa place et son espacement viennent d'`Intl`
|
|
10
|
+
// (« 1 234,56 € » en français, « $1,234.56 » en anglais), la valeur reste éditable. Les
|
|
11
|
+
// bornes (`min` / `max`) et l'arrondi de la devise s'appliquent à la sortie du champ ; les
|
|
12
|
+
// flèches ↑ / ↓ avancent du pas (`step`, ×10 avec Maj).
|
|
13
|
+
// Tout le calcul vit dans `lib/currency.ts` (pur, testé).
|
|
14
|
+
import { computed, ref, watch } from "vue"
|
|
15
|
+
import { Icon } from "@iconify/vue"
|
|
16
|
+
import {
|
|
17
|
+
applyLimits,
|
|
18
|
+
caretForDigitCount,
|
|
19
|
+
currencyFormat,
|
|
20
|
+
digitCountBefore,
|
|
21
|
+
draftFromValue,
|
|
22
|
+
draftValue,
|
|
23
|
+
formatAmount,
|
|
24
|
+
formatDraft,
|
|
25
|
+
localeOf,
|
|
26
|
+
parseAmount,
|
|
27
|
+
stepValue,
|
|
28
|
+
type AmountDraft,
|
|
29
|
+
} from "../lib/currency"
|
|
30
|
+
import { icons } from "../lib/icons"
|
|
31
|
+
import { cn } from "../lib/utils"
|
|
32
|
+
import { radiusStyle, useConfigLang, useRadius } from "../lib/useComponentProps"
|
|
33
|
+
import type { RadiusProp } from "../lib/useComponentProps"
|
|
34
|
+
|
|
35
|
+
interface Props {
|
|
36
|
+
/** Montant — `null` quand le champ est vide */
|
|
37
|
+
modelValue?: number | null
|
|
38
|
+
/** Code ISO 4217 (défaut `"EUR"`) — `""` pour un montant sans devise */
|
|
39
|
+
currency?: string
|
|
40
|
+
/** Locale BCP-47 (défaut : la langue de QConfigProvider — `fr` → `fr-FR`, `en` → `en-US`) */
|
|
41
|
+
locale?: string
|
|
42
|
+
/** Décimales (défaut : celles de la devise — 2 pour EUR, 0 pour JPY) */
|
|
43
|
+
decimals?: number
|
|
44
|
+
/** Affichage de la devise : symbole (défaut), code ISO ou nom localisé */
|
|
45
|
+
currencyDisplay?: "symbol" | "code" | "name"
|
|
46
|
+
/** Autorise un montant négatif */
|
|
47
|
+
allowNegative?: boolean
|
|
48
|
+
/** Borne basse, appliquée à la sortie du champ */
|
|
49
|
+
min?: number
|
|
50
|
+
/** Borne haute, appliquée à la sortie du champ */
|
|
51
|
+
max?: number
|
|
52
|
+
/** Pas des flèches ↑ / ↓ (×10 avec Maj) */
|
|
53
|
+
step?: number
|
|
54
|
+
/** Label affiché au-dessus du champ */
|
|
55
|
+
label?: string
|
|
56
|
+
/** Aide affichée sous le champ */
|
|
57
|
+
hint?: string
|
|
58
|
+
/** État d'erreur */
|
|
59
|
+
error?: boolean
|
|
60
|
+
/** Message d'erreur (affiché quand error) */
|
|
61
|
+
errorMessage?: string
|
|
62
|
+
/** Bouton d'effacement (remet `null`) */
|
|
63
|
+
clearable?: boolean
|
|
64
|
+
/** Bordure visible */
|
|
65
|
+
outlined?: boolean
|
|
66
|
+
/** Fond gris, soulignement */
|
|
67
|
+
filled?: boolean
|
|
68
|
+
/** Aucune bordure */
|
|
69
|
+
borderless?: boolean
|
|
70
|
+
/** Coins arrondis : true = pilule, ou échelle xs|sm|md|lg */
|
|
71
|
+
radius?: RadiusProp
|
|
72
|
+
/** Hauteur réduite */
|
|
73
|
+
dense?: boolean
|
|
74
|
+
disable?: boolean
|
|
75
|
+
readonly?: boolean
|
|
76
|
+
placeholder?: string
|
|
77
|
+
/** Icône Iconify à gauche (remplacée par le slot #prepend) */
|
|
78
|
+
iconLeft?: string
|
|
79
|
+
/** Icône Iconify à droite (remplacée par le slot #append) */
|
|
80
|
+
iconRight?: string
|
|
81
|
+
/** Nom du champ : un input caché porte la valeur brute pour l'envoi de formulaire */
|
|
82
|
+
name?: string
|
|
83
|
+
/** Id de l'input natif (pour un `<label for>`) */
|
|
84
|
+
id?: string
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
88
|
+
modelValue: null,
|
|
89
|
+
currency: "EUR",
|
|
90
|
+
locale: "",
|
|
91
|
+
currencyDisplay: "symbol",
|
|
92
|
+
allowNegative: false,
|
|
93
|
+
step: 1,
|
|
94
|
+
error: false,
|
|
95
|
+
clearable: false,
|
|
96
|
+
outlined: false,
|
|
97
|
+
filled: false,
|
|
98
|
+
borderless: false,
|
|
99
|
+
dense: false,
|
|
100
|
+
disable: false,
|
|
101
|
+
readonly: false,
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
const emit = defineEmits<{
|
|
105
|
+
"update:modelValue": [value: number | null]
|
|
106
|
+
focus: [event: FocusEvent]
|
|
107
|
+
blur: [event: FocusEvent]
|
|
108
|
+
clear: []
|
|
109
|
+
}>()
|
|
110
|
+
|
|
111
|
+
const lang = useConfigLang()
|
|
112
|
+
const effectiveRadius = useRadius("QInputCurrency", () => props.radius)
|
|
113
|
+
const roundedStyle = computed(() => radiusStyle(effectiveRadius.value))
|
|
114
|
+
|
|
115
|
+
/** Réglages de formatage : locale, devise, décimales, bornes */
|
|
116
|
+
const options = computed(() => ({
|
|
117
|
+
locale: props.locale || localeOf(lang.value),
|
|
118
|
+
currency: props.currency,
|
|
119
|
+
decimals: props.decimals,
|
|
120
|
+
allowNegative: props.allowNegative,
|
|
121
|
+
currencyDisplay: props.currencyDisplay,
|
|
122
|
+
min: props.min,
|
|
123
|
+
max: props.max,
|
|
124
|
+
}))
|
|
125
|
+
|
|
126
|
+
const format = computed(() => currencyFormat(options.value))
|
|
127
|
+
|
|
128
|
+
const focused = ref(false)
|
|
129
|
+
const nativeEl = ref<HTMLInputElement | null>(null)
|
|
130
|
+
/** État de la frappe (indépendant de la locale) */
|
|
131
|
+
const draft = ref<AmountDraft>({ negative: false, integer: "", fraction: "", hasSeparator: false })
|
|
132
|
+
|
|
133
|
+
// Le modèle fait foi : au montage et à chaque changement venu de l'extérieur, on repart de
|
|
134
|
+
// la valeur — sauf quand l'utilisateur est en train de taper (on ne l'interrompt pas).
|
|
135
|
+
watch(
|
|
136
|
+
() => props.modelValue,
|
|
137
|
+
(value) => {
|
|
138
|
+
if (focused.value) return
|
|
139
|
+
if (draftValue(draft.value) === value) return
|
|
140
|
+
draft.value = draftFromValue(value, options.value)
|
|
141
|
+
},
|
|
142
|
+
{ immediate: true },
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
// La devise ou la locale changent : l'affichage se recalcule et la valeur est ramenée aux
|
|
146
|
+
// nouvelles décimales (un montant en JPY n'a pas de centimes) — le modèle reste cohérent
|
|
147
|
+
// avec ce que le champ affiche.
|
|
148
|
+
watch(format, () => {
|
|
149
|
+
const limited = applyLimits(props.modelValue, options.value)
|
|
150
|
+
if (limited !== props.modelValue) emit("update:modelValue", limited)
|
|
151
|
+
if (focused.value) return
|
|
152
|
+
draft.value = draftFromValue(limited, options.value)
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
const displayValue = computed(() =>
|
|
156
|
+
focused.value ? formatDraft(draft.value, options.value) : formatAmount(props.modelValue, options.value),
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
const showClear = computed(
|
|
160
|
+
() =>
|
|
161
|
+
props.clearable &&
|
|
162
|
+
!props.disable &&
|
|
163
|
+
!props.readonly &&
|
|
164
|
+
(props.modelValue !== null || draft.value.integer !== "" || draft.value.fraction !== ""),
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
const fieldClasses = computed(() =>
|
|
168
|
+
cn(
|
|
169
|
+
"q-input",
|
|
170
|
+
props.outlined && "q-field--outlined",
|
|
171
|
+
props.filled && "q-field--filled",
|
|
172
|
+
props.borderless && "q-field--borderless",
|
|
173
|
+
effectiveRadius.value === true && "q-field--rounded",
|
|
174
|
+
props.dense && "q-field--dense",
|
|
175
|
+
props.error && "q-field--error",
|
|
176
|
+
props.disable && "q-field--disabled",
|
|
177
|
+
),
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
const inputAttrs = computed(() => ({
|
|
181
|
+
type: "text",
|
|
182
|
+
inputmode: "decimal" as const,
|
|
183
|
+
autocomplete: "off",
|
|
184
|
+
id: props.id || undefined,
|
|
185
|
+
placeholder: props.placeholder,
|
|
186
|
+
disabled: props.disable || undefined,
|
|
187
|
+
readonly: props.readonly || undefined,
|
|
188
|
+
"aria-invalid": props.error || undefined,
|
|
189
|
+
}))
|
|
190
|
+
|
|
191
|
+
/** Écrit la valeur formatée dans l'input et replace le curseur après les mêmes chiffres */
|
|
192
|
+
const writeDisplay = (digitsBefore: number) => {
|
|
193
|
+
const el = nativeEl.value
|
|
194
|
+
if (!el) return
|
|
195
|
+
const display = formatDraft(draft.value, options.value)
|
|
196
|
+
el.value = display
|
|
197
|
+
const caret = caretForDigitCount(display, digitsBefore)
|
|
198
|
+
el.setSelectionRange(caret, caret)
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const onInput = (event: Event) => {
|
|
202
|
+
const el = event.target as HTMLInputElement
|
|
203
|
+
const caret = el.selectionStart ?? el.value.length
|
|
204
|
+
const digitsBefore = digitCountBefore(el.value, caret)
|
|
205
|
+
|
|
206
|
+
draft.value = parseAmount(el.value, options.value)
|
|
207
|
+
const value = draftValue(draft.value)
|
|
208
|
+
if (value !== props.modelValue) emit("update:modelValue", value)
|
|
209
|
+
|
|
210
|
+
// La saisie est reformatée immédiatement (même sans `v-model` : le champ ne peut pas
|
|
211
|
+
// contenir de texte non formaté)
|
|
212
|
+
writeDisplay(digitsBefore)
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const onFocus = (event: FocusEvent) => {
|
|
216
|
+
focused.value = true
|
|
217
|
+
draft.value = parseAmount(
|
|
218
|
+
props.modelValue === null
|
|
219
|
+
? formatDraft(draft.value, options.value)
|
|
220
|
+
: formatAmount(props.modelValue, options.value),
|
|
221
|
+
options.value,
|
|
222
|
+
)
|
|
223
|
+
emit("focus", event)
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const onBlur = (event: FocusEvent) => {
|
|
227
|
+
focused.value = false
|
|
228
|
+
const limited = applyLimits(draftValue(draft.value), options.value)
|
|
229
|
+
draft.value = draftFromValue(limited, options.value)
|
|
230
|
+
if (limited !== props.modelValue) emit("update:modelValue", limited)
|
|
231
|
+
emit("blur", event)
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const onKeydown = (event: KeyboardEvent) => {
|
|
235
|
+
if (props.disable || props.readonly) return
|
|
236
|
+
if (event.key !== "ArrowUp" && event.key !== "ArrowDown") return
|
|
237
|
+
|
|
238
|
+
event.preventDefault()
|
|
239
|
+
const next = stepValue(
|
|
240
|
+
draftValue(draft.value),
|
|
241
|
+
props.step,
|
|
242
|
+
event.key === "ArrowUp" ? 1 : -1,
|
|
243
|
+
event.shiftKey,
|
|
244
|
+
options.value,
|
|
245
|
+
)
|
|
246
|
+
draft.value = draftFromValue(next, options.value)
|
|
247
|
+
// Curseur en fin de saisie après le pas (tous les chiffres du brouillon)
|
|
248
|
+
writeDisplay(draft.value.integer.length + draft.value.fraction.length)
|
|
249
|
+
if (next !== props.modelValue) emit("update:modelValue", next)
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const onClear = () => {
|
|
253
|
+
draft.value = { negative: false, integer: "", fraction: "", hasSeparator: false }
|
|
254
|
+
emit("update:modelValue", null)
|
|
255
|
+
emit("clear")
|
|
256
|
+
nativeEl.value?.focus()
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/** Décale le groupe et l'espace : « 1 234,56 € » ou « $1,234.56 » */
|
|
260
|
+
const symbolBefore = computed(() => format.value.symbol !== "" && format.value.position === "before")
|
|
261
|
+
const symbolAfter = computed(() => format.value.symbol !== "" && format.value.position === "after")
|
|
262
|
+
</script>
|
|
263
|
+
|
|
264
|
+
<template>
|
|
265
|
+
<div class="q-input q-input-currency" :class="fieldClasses" :style="roundedStyle">
|
|
266
|
+
<label v-if="label" class="q-field__label-stack" :for="id">{{ label }}</label>
|
|
267
|
+
|
|
268
|
+
<div class="q-field__control">
|
|
269
|
+
<slot name="prepend">
|
|
270
|
+
<Icon v-if="iconLeft" :icon="iconLeft" class="q-field__icon" aria-hidden="true" />
|
|
271
|
+
</slot>
|
|
272
|
+
|
|
273
|
+
<span v-if="symbolBefore" class="q-field__currency q-field__currency--before">{{
|
|
274
|
+
format.symbol
|
|
275
|
+
}}</span>
|
|
276
|
+
|
|
277
|
+
<input
|
|
278
|
+
ref="nativeEl"
|
|
279
|
+
class="q-field__native"
|
|
280
|
+
:value="displayValue"
|
|
281
|
+
v-bind="inputAttrs"
|
|
282
|
+
@input="onInput"
|
|
283
|
+
@focus="onFocus"
|
|
284
|
+
@blur="onBlur"
|
|
285
|
+
@keydown="onKeydown"
|
|
286
|
+
/>
|
|
287
|
+
|
|
288
|
+
<span v-if="symbolAfter" class="q-field__currency q-field__currency--after">{{
|
|
289
|
+
format.symbol
|
|
290
|
+
}}</span>
|
|
291
|
+
|
|
292
|
+
<button
|
|
293
|
+
v-if="showClear"
|
|
294
|
+
class="q-field__clear"
|
|
295
|
+
type="button"
|
|
296
|
+
aria-label="Effacer"
|
|
297
|
+
@click="onClear"
|
|
298
|
+
>
|
|
299
|
+
<Icon :icon="icons.x" aria-hidden="true" />
|
|
300
|
+
</button>
|
|
301
|
+
|
|
302
|
+
<slot name="append">
|
|
303
|
+
<Icon v-if="iconRight" :icon="iconRight" class="q-field__icon" aria-hidden="true" />
|
|
304
|
+
</slot>
|
|
305
|
+
</div>
|
|
306
|
+
|
|
307
|
+
<div class="q-field__bottom">
|
|
308
|
+
<div v-if="error" class="q-field__error">
|
|
309
|
+
<slot name="error">{{ errorMessage }}</slot>
|
|
310
|
+
</div>
|
|
311
|
+
<div v-else-if="hint || $slots.hint" class="q-field__hint">
|
|
312
|
+
<slot name="hint">{{ hint }}</slot>
|
|
313
|
+
</div>
|
|
314
|
+
</div>
|
|
315
|
+
|
|
316
|
+
<!-- Valeur brute : un formulaire classique envoie un nombre, pas un montant formaté -->
|
|
317
|
+
<input v-if="name" type="hidden" :name="name" :value="modelValue === null ? '' : String(modelValue)" />
|
|
318
|
+
</div>
|
|
319
|
+
</template>
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// QItemLabel — API Quasar : <q-item-label overline caption header :lines="2" color="primary">
|
|
3
|
+
// Libellé de texte d'un QItem : variantes overline / caption / header,
|
|
4
|
+
// troncature multi-lignes (lines) et couleur (token ou hex).
|
|
5
|
+
import { cn } from "../lib/utils"
|
|
6
|
+
import { colorValue } from "../lib/colors"
|
|
7
|
+
|
|
8
|
+
interface Props {
|
|
9
|
+
/** Petites majuscules, lettrage espacé (étiquette au-dessus) */
|
|
10
|
+
overline?: boolean
|
|
11
|
+
/** Texte secondaire (couleur atténuée) */
|
|
12
|
+
caption?: boolean
|
|
13
|
+
/** Titre d'en-tête */
|
|
14
|
+
header?: boolean
|
|
15
|
+
/** Tronque après N lignes (`-webkit-line-clamp`) — 0 = pas de troncature */
|
|
16
|
+
lines?: number
|
|
17
|
+
/** Couleur du texte (token ou hex) */
|
|
18
|
+
color?: string
|
|
19
|
+
/** Élément rendu (div par défaut) */
|
|
20
|
+
tag?: string
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
24
|
+
overline: false,
|
|
25
|
+
caption: false,
|
|
26
|
+
header: false,
|
|
27
|
+
lines: 0,
|
|
28
|
+
color: "",
|
|
29
|
+
tag: "div",
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
const labelClasses = cn(
|
|
33
|
+
"q-item__label",
|
|
34
|
+
props.caption && "q-item__label--caption",
|
|
35
|
+
props.overline && "q-item__label--overline",
|
|
36
|
+
props.header && "q-item__label--header",
|
|
37
|
+
props.lines > 0 && "q-item__label--clamp",
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
const labelStyle: Record<string, string> = {
|
|
41
|
+
...(props.color ? { color: colorValue(props.color) } : {}),
|
|
42
|
+
...(props.lines > 0 ? { "--q-item-label-lines": String(props.lines) } : {}),
|
|
43
|
+
}
|
|
44
|
+
</script>
|
|
45
|
+
|
|
46
|
+
<template>
|
|
47
|
+
<component
|
|
48
|
+
:is="tag"
|
|
49
|
+
class="q-item__label"
|
|
50
|
+
:class="labelClasses"
|
|
51
|
+
:style="labelStyle"
|
|
52
|
+
v-bind="$attrs"
|
|
53
|
+
>
|
|
54
|
+
<slot />
|
|
55
|
+
</component>
|
|
56
|
+
</template>
|
|
57
|
+
|
|
58
|
+
<style scoped>
|
|
59
|
+
.q-item__label {
|
|
60
|
+
font-size: 14px;
|
|
61
|
+
line-height: 1.35;
|
|
62
|
+
color: var(--foreground);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/* ─── caption : texte secondaire atténué ─── */
|
|
66
|
+
.q-item__label--caption {
|
|
67
|
+
font-size: 12.5px;
|
|
68
|
+
color: var(--muted-foreground);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/* ─── overline : petites majuscules, lettrage espacé ─── */
|
|
72
|
+
.q-item__label--overline {
|
|
73
|
+
text-transform: uppercase;
|
|
74
|
+
letter-spacing: 0.1em;
|
|
75
|
+
font-size: 10px;
|
|
76
|
+
font-weight: 600;
|
|
77
|
+
line-height: 1.2;
|
|
78
|
+
color: var(--muted-foreground);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/* ─── header : titre de section ─── */
|
|
82
|
+
.q-item__label--header {
|
|
83
|
+
font-size: 13px;
|
|
84
|
+
font-weight: 600;
|
|
85
|
+
letter-spacing: 0.01em;
|
|
86
|
+
color: var(--muted-foreground);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/* ─── troncature multi-lignes avec … (line-clamp) ─── */
|
|
90
|
+
.q-item__label--clamp {
|
|
91
|
+
display: -webkit-box;
|
|
92
|
+
-webkit-box-orient: vertical;
|
|
93
|
+
-webkit-line-clamp: var(--q-item-label-lines, 1);
|
|
94
|
+
line-clamp: var(--q-item-label-lines, 1);
|
|
95
|
+
overflow: hidden;
|
|
96
|
+
text-overflow: ellipsis;
|
|
97
|
+
word-break: break-word;
|
|
98
|
+
}
|
|
99
|
+
</style>
|