@tekkare/romulus 0.1.205 → 0.1.207

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/dist/module.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "compatibility": {
5
5
  "nuxt": ">=3.16.0"
6
6
  },
7
- "version": "0.1.204",
7
+ "version": "0.1.206",
8
8
  "builder": {
9
9
  "@nuxt/module-builder": "0.8.4",
10
10
  "unbuild": "2.0.0"
package/dist/module.mjs CHANGED
@@ -51,6 +51,8 @@ const module = defineNuxtModule({
51
51
  if (!nuxt.options.app.head.link.some((l) => l.href === phosphorFillHref)) {
52
52
  nuxt.options.app.head.link.push({ rel: "stylesheet", href: phosphorFillHref });
53
53
  }
54
+ const optimizeDeps = nuxt.options.vite.optimizeDeps ||= {};
55
+ optimizeDeps.include = [...optimizeDeps.include || [], "@phosphor-icons/vue"];
54
56
  }
55
57
  });
56
58
 
@@ -21,11 +21,17 @@ const props = withDefaults(defineProps<{
21
21
  * ce qui suffit des que les barres sont nombreuses.
22
22
  */
23
23
  labelFormatter?: RmBarLabelFormatter
24
+ /**
25
+ * Formate l'axe des valeurs et l'infobulle. Independant de `labelFormatter`,
26
+ * qui seul decide si les barres portent une etiquette.
27
+ */
28
+ valueFormatter?: (value: number) => string
24
29
  }>(), {
25
30
  horizontal: false,
26
31
  height: 'standard',
27
32
  description: undefined,
28
33
  labelFormatter: undefined,
34
+ valueFormatter: undefined,
29
35
  })
30
36
 
31
37
  const { theme, palette, decal } = useChartTheme()
@@ -39,6 +45,7 @@ const option = computed(() =>
39
45
  palette.value.cat,
40
46
  decal.value,
41
47
  props.labelFormatter,
48
+ props.valueFormatter,
42
49
  )
43
50
  : rmBarVerticalOption(
44
51
  props.categories,
@@ -47,6 +54,7 @@ const option = computed(() =>
47
54
  palette.value.cat,
48
55
  decal.value,
49
56
  props.labelFormatter,
57
+ props.valueFormatter,
50
58
  ),
51
59
  )
52
60
  </script>
@@ -58,6 +58,11 @@ const props = withDefaults(
58
58
  /** Hauteur du dessin, en pixels. */
59
59
  height?: number
60
60
  lang?: 'fr' | 'en'
61
+ /**
62
+ * Formate les valeurs de l'infobulle et des bornes de la legende, a la
63
+ * place du format de la langue. Pour une app qui a sa regle d'arrondi.
64
+ */
65
+ valueFormatter?: (value: number) => string
61
66
  }>(),
62
67
  {
63
68
  data: () => [],
@@ -67,6 +72,7 @@ const props = withDefaults(
67
72
  compact: false,
68
73
  height: 500,
69
74
  lang: 'fr',
75
+ valueFormatter: undefined,
70
76
  },
71
77
  )
72
78
 
@@ -186,7 +192,11 @@ const T = computed(() =>
186
192
  /** Dégradé de la légende, dans l'ordre de lecture de l'échelle. */
187
193
  const legendGradient = computed(() => `linear-gradient(90deg, ${ramp.value.join(', ')})`)
188
194
 
189
- const fmt = computed(() => new Intl.NumberFormat(props.lang === 'en' ? 'en-US' : 'fr-FR'))
195
+ const fmt = computed(() =>
196
+ props.valueFormatter
197
+ ? { format: props.valueFormatter }
198
+ : new Intl.NumberFormat(props.lang === 'en' ? 'en-US' : 'fr-FR'),
199
+ )
190
200
  </script>
191
201
 
192
202
  <template>
@@ -9,44 +9,36 @@
9
9
  icone deja chargee : une liste de trois cents lignes posait trois cents
10
10
  attentes, et les icones apparaissaient apres le texte.
11
11
  -->
12
- <script setup lang="ts">
13
- import { computed, defineAsyncComponent, shallowRef, type Component } from 'vue'
14
-
15
- export interface RmIconProps {
16
- /** Nom Phosphor sans le prefixe « Ph » (« House », « Gear », « ChartBar »). */
17
- name: string
18
- /** Taille en pixels. */
19
- size?: number
20
- /** Graisse du trait. `fill` donne la forme pleine. */
21
- weight?: 'thin' | 'light' | 'regular' | 'bold' | 'fill' | 'duotone'
22
- color?: string
23
- }
24
-
25
- const props = withDefaults(defineProps<RmIconProps>(), {
26
- size: 20,
27
- weight: 'regular',
28
- color: undefined,
29
- })
12
+ <script lang="ts">
13
+ import { defineAsyncComponent, type Component } from 'vue'
30
14
 
31
15
  /**
32
- * Un composant asynchrone par nom d'icone, partage par toutes les instances.
33
- * Une fois le paquet charge, le composant garde sa resolution et se pose sans
34
- * attente au montage suivant.
16
+ * Etat partage par toutes les instances : il vit dans le module, pas dans
17
+ * `<script setup>` (qui le recreait a chaque instance, donc une attente par
18
+ * icone).
35
19
  */
36
20
  const cache = new Map<string, Component>()
37
-
38
- /** Le module, demande une seule fois quel que soit le nombre d'icones. */
39
- const iconModule = shallowRef<Promise<Record<string, Component>>>()
21
+ let iconModule: Promise<Record<string, Component>> | undefined
22
+ /** Le paquet une fois charge : les icones se posent alors sans attente. */
23
+ let loaded: Record<string, Component> | undefined
40
24
 
41
25
  function loadIcons() {
42
- if (!iconModule.value) {
43
- iconModule.value = import('@phosphor-icons/vue') as unknown as Promise<Record<string, Component>>
26
+ if (!iconModule) {
27
+ iconModule = (import('@phosphor-icons/vue') as unknown as Promise<Record<string, Component>>)
28
+ .then((mod) => {
29
+ loaded = mod
30
+ return mod
31
+ })
44
32
  }
45
- return iconModule.value
33
+ return iconModule
46
34
  }
47
35
 
36
+ // Le paquet est demande des l'import du composant, pas au premier montage.
37
+ if (typeof window !== 'undefined') loadIcons()
38
+
48
39
  function resolve(name: string) {
49
40
  const key = `Ph${name}`
41
+ if (loaded) return loaded[key] || loaded.PhQuestion
50
42
  let component = cache.get(key)
51
43
  if (!component) {
52
44
  component = defineAsyncComponent(() =>
@@ -56,6 +48,26 @@ function resolve(name: string) {
56
48
  }
57
49
  return component
58
50
  }
51
+ </script>
52
+
53
+ <script setup lang="ts">
54
+ import { computed } from 'vue'
55
+
56
+ export interface RmIconProps {
57
+ /** Nom Phosphor sans le prefixe « Ph » (« House », « Gear », « ChartBar »). */
58
+ name: string
59
+ /** Taille en pixels. */
60
+ size?: number
61
+ /** Graisse du trait. `fill` donne la forme pleine. */
62
+ weight?: 'thin' | 'light' | 'regular' | 'bold' | 'fill' | 'duotone'
63
+ color?: string
64
+ }
65
+
66
+ const props = withDefaults(defineProps<RmIconProps>(), {
67
+ size: 20,
68
+ weight: 'regular',
69
+ color: undefined,
70
+ })
59
71
 
60
72
  const iconComponent = computed(() => resolve(props.name))
61
73
  </script>
@@ -52,6 +52,12 @@ const props = withDefaults(defineProps<{
52
52
  * nombre entier avec ses separateurs de milliers.
53
53
  */
54
54
  numberFormat?: 'compact' | 'plain'
55
+ /**
56
+ * Formateur de l'appelant, pour l'axe, les etiquettes ET l'infobulle. Passe
57
+ * avant `numberFormat` : une app qui a sa propre regle d'arrondi la veut
58
+ * partout, y compris au survol.
59
+ */
60
+ valueFormatter?: (value: number) => string
55
61
  height?: keyof typeof RM_CHART_HEIGHTS | number
56
62
  description?: string
57
63
  }>(), {
@@ -64,6 +70,7 @@ const props = withDefaults(defineProps<{
64
70
  legendPosition: 'bottom',
65
71
  startAtZero: false,
66
72
  numberFormat: undefined,
73
+ valueFormatter: undefined,
67
74
  height: 'standard',
68
75
  description: undefined,
69
76
  })
@@ -71,6 +78,7 @@ const props = withDefaults(defineProps<{
71
78
  const { theme, palette, decal } = useChartTheme()
72
79
 
73
80
  const formatter = computed(() => {
81
+ if (props.valueFormatter) return props.valueFormatter
74
82
  if (props.numberFormat === 'compact') return rmCompact
75
83
  if (props.numberFormat === 'plain') return rmNumber
76
84
  return undefined
@@ -100,6 +108,7 @@ const option = computed(() => {
100
108
  legendPosition: props.legendPosition,
101
109
  startAtZero: props.startAtZero,
102
110
  valueFormatter: formatter.value,
111
+ tooltipFormatter: props.valueFormatter,
103
112
  }) as Record<string, any>
104
113
  if (built.legend && Object.keys(legendSelected.value).length) {
105
114
  built.legend = { ...built.legend, selected: { ...legendSelected.value } }
@@ -200,8 +200,24 @@ export interface RmSeriesInput {
200
200
  * (« 1 240 (18 %) »), et c'est l'index qui la retrouve.
201
201
  */
202
202
  export type RmBarLabelFormatter = (value: number, index: number) => string;
203
- export declare function rmBarVerticalOption(categories: (string | number)[], series: RmSeriesInput[], theme: RmChartTheme, palette: string[], decal?: boolean, formatter?: RmBarLabelFormatter): {
203
+ export declare function rmBarVerticalOption(categories: (string | number)[], series: RmSeriesInput[], theme: RmChartTheme, palette: string[], decal?: boolean, formatter?: RmBarLabelFormatter, valueFormatter?: (value: number) => string): {
204
204
  tooltip: {
205
+ valueFormatter: (value: unknown) => string;
206
+ trigger: string;
207
+ backgroundColor: string;
208
+ textStyle: {
209
+ color: string;
210
+ fontFamily: string;
211
+ fontSize: number;
212
+ };
213
+ borderColor: string;
214
+ borderWidth: number;
215
+ appendTo: string;
216
+ confine: boolean;
217
+ enterable: boolean;
218
+ extraCssText: string;
219
+ } | {
220
+ valueFormatter?: undefined;
205
221
  trigger: string;
206
222
  backgroundColor: string;
207
223
  textStyle: {
@@ -270,6 +286,7 @@ export declare function rmBarVerticalOption(categories: (string | number)[], ser
270
286
  show: boolean;
271
287
  };
272
288
  axisLabel: {
289
+ formatter?: ((value: number) => string) | undefined;
273
290
  fontSize: number;
274
291
  color: string;
275
292
  fontFamily: string;
@@ -284,6 +301,7 @@ export declare function rmBarVerticalOption(categories: (string | number)[], ser
284
301
  };
285
302
  yAxis: {
286
303
  axisLabel: {
304
+ formatter?: ((value: number) => string) | undefined;
287
305
  fontSize: number;
288
306
  color: string;
289
307
  fontFamily: string;
@@ -334,7 +352,38 @@ export declare function rmBarVerticalOption(categories: (string | number)[], ser
334
352
  };
335
353
  };
336
354
  /** Bar chart horizontal, avec valeur affichee en bout de barre. */
337
- export declare function rmBarHorizontalOption(labels: string[], values: (number | null)[], theme: RmChartTheme, palette: string[], decal?: boolean, formatter?: RmBarLabelFormatter): {
355
+ export declare function rmBarHorizontalOption(labels: string[], values: (number | null)[], theme: RmChartTheme, palette: string[], decal?: boolean, formatter?: RmBarLabelFormatter, valueFormatter?: (value: number) => string): {
356
+ tooltip: {
357
+ valueFormatter: (value: unknown) => string;
358
+ trigger: string;
359
+ backgroundColor: string;
360
+ textStyle: {
361
+ color: string;
362
+ fontFamily: string;
363
+ fontSize: number;
364
+ };
365
+ borderColor: string;
366
+ borderWidth: number;
367
+ appendTo: string;
368
+ confine: boolean;
369
+ enterable: boolean;
370
+ extraCssText: string;
371
+ } | {
372
+ valueFormatter?: undefined;
373
+ trigger: string;
374
+ backgroundColor: string;
375
+ textStyle: {
376
+ color: string;
377
+ fontFamily: string;
378
+ fontSize: number;
379
+ };
380
+ borderColor: string;
381
+ borderWidth: number;
382
+ appendTo: string;
383
+ confine: boolean;
384
+ enterable: boolean;
385
+ extraCssText: string;
386
+ };
338
387
  grid: {
339
388
  left: number;
340
389
  right: number;
@@ -344,6 +393,7 @@ export declare function rmBarHorizontalOption(labels: string[], values: (number
344
393
  };
345
394
  xAxis: {
346
395
  axisLabel: {
396
+ formatter?: ((value: number) => string) | undefined;
347
397
  fontSize: number;
348
398
  color: string;
349
399
  fontFamily: string;
@@ -365,6 +415,7 @@ export declare function rmBarHorizontalOption(labels: string[], values: (number
365
415
  show: boolean;
366
416
  };
367
417
  axisLabel: {
418
+ formatter?: ((value: number) => string) | undefined;
368
419
  fontSize: number;
369
420
  color: string;
370
421
  fontFamily: string;
@@ -409,21 +460,6 @@ export declare function rmBarHorizontalOption(labels: string[], values: (number
409
460
  textStyle: {
410
461
  fontFamily: string;
411
462
  };
412
- tooltip: {
413
- trigger: string;
414
- backgroundColor: string;
415
- textStyle: {
416
- color: string;
417
- fontFamily: string;
418
- fontSize: number;
419
- };
420
- borderColor: string;
421
- borderWidth: number;
422
- appendTo: string;
423
- confine: boolean;
424
- enterable: boolean;
425
- extraCssText: string;
426
- };
427
463
  };
428
464
  /** Line ou area chart. `step` produit la marche d'escalier des series de prix. */
429
465
  /**
@@ -471,6 +507,11 @@ export declare function rmLineOption(categories: (string | number)[], series: Rm
471
507
  startAtZero?: boolean;
472
508
  /** Formate les valeurs, dans les etiquettes comme dans l'infobulle. */
473
509
  valueFormatter?: (value: number) => string;
510
+ /**
511
+ * Formate l'infobulle a la place de `rmNumber`. Une app qui impose sa
512
+ * propre regle d'arrondi la veut aussi au survol, pas seulement sur l'axe.
513
+ */
514
+ tooltipFormatter?: (value: number) => string;
474
515
  }): {
475
516
  animation: boolean;
476
517
  tooltip: {
@@ -544,6 +585,7 @@ export declare function rmLineOption(categories: (string | number)[], series: Rm
544
585
  };
545
586
  boundaryGap: boolean;
546
587
  axisLabel: {
588
+ formatter?: ((value: number) => string) | undefined;
547
589
  fontSize: number;
548
590
  color: string;
549
591
  fontFamily: string;
@@ -150,21 +150,26 @@ const valueAxis = (theme, label, hidden = false, formatter) => ({
150
150
  axisTick: hidden ? { show: false } : void 0,
151
151
  splitLine: { lineStyle: { color: theme.splitLine } }
152
152
  });
153
- const axisCommon = (theme) => ({
154
- axisLabel: { fontSize: 11, color: theme.mutedColor, fontFamily: FONT },
153
+ const axisCommon = (theme, formatter) => ({
154
+ axisLabel: { fontSize: 11, color: theme.mutedColor, fontFamily: FONT, ...formatter ? { formatter } : {} },
155
155
  axisLine: { lineStyle: { color: theme.axisLine } },
156
156
  splitLine: { lineStyle: { color: theme.splitLine } }
157
157
  });
158
- export function rmBarVerticalOption(categories, series, theme, palette, decal = false, formatter) {
158
+ const rmTooltipValue = (formatter) => formatter ? { valueFormatter: (value) => typeof value === "number" ? formatter(value) : "" } : {};
159
+ export function rmBarVerticalOption(categories, series, theme, palette, decal = false, formatter, valueFormatter) {
159
160
  return {
160
161
  ...rmBaseOption(theme, decal),
161
- tooltip: { ...rmBaseOption(theme, decal).tooltip, trigger: rmTooltipTrigger(series.length) },
162
+ tooltip: {
163
+ ...rmBaseOption(theme, decal).tooltip,
164
+ trigger: rmTooltipTrigger(series.length),
165
+ ...rmTooltipValue(valueFormatter)
166
+ },
162
167
  legend: series.length > 1 ? rmLegend(theme, series.length, "bottom", series.map((s) => String(s.name ?? ""))) : void 0,
163
168
  // La legende passee sous le graphe prend sa place dans la gouttiere basse,
164
169
  // sinon elle recouvre les libelles de l'axe des abscisses.
165
170
  grid: { ...rmBaseOption(theme, decal).grid, bottom: series.length > 1 ? 56 : 36 },
166
171
  xAxis: { type: "category", data: categories, ...axisCommon(theme), splitLine: { show: false } },
167
- yAxis: { type: "value", ...axisCommon(theme) },
172
+ yAxis: { type: "value", ...axisCommon(theme, valueFormatter) },
168
173
  series: series.map((s, i) => ({
169
174
  type: "bar",
170
175
  name: s.name,
@@ -184,11 +189,12 @@ export function rmBarVerticalOption(categories, series, theme, palette, decal =
184
189
  }))
185
190
  };
186
191
  }
187
- export function rmBarHorizontalOption(labels, values, theme, palette, decal = false, formatter) {
192
+ export function rmBarHorizontalOption(labels, values, theme, palette, decal = false, formatter, valueFormatter) {
188
193
  return {
189
194
  ...rmBaseOption(theme, decal),
195
+ tooltip: { ...rmBaseOption(theme, decal).tooltip, ...rmTooltipValue(valueFormatter) },
190
196
  grid: { left: 140, right: 60, top: 16, bottom: 24, containLabel: true },
191
- xAxis: { type: "value", ...axisCommon(theme) },
197
+ xAxis: { type: "value", ...axisCommon(theme, valueFormatter) },
192
198
  yAxis: { type: "category", data: labels, ...axisCommon(theme), splitLine: { show: false } },
193
199
  series: [{
194
200
  type: "bar",
@@ -203,7 +209,9 @@ export function rmBarHorizontalOption(labels, values, theme, palette, decal = fa
203
209
  fontSize: 11,
204
210
  color: theme.mutedColor,
205
211
  fontFamily: FONT,
206
- formatter: formatter ? (p) => formatter(p.value, p.dataIndex) : void 0
212
+ // L'etiquette est toujours posee ici : sans formateur d'etiquette, elle
213
+ // suit au moins celui des valeurs plutot que le nombre brut.
214
+ formatter: formatter ? (p) => formatter(p.value, p.dataIndex) : valueFormatter ? (p) => valueFormatter(p.value) : void 0
207
215
  }
208
216
  }]
209
217
  };
@@ -220,7 +228,8 @@ export function rmLineOption(categories, series, theme, palette, options = {}) {
220
228
  hideValueAxis = false,
221
229
  legendPosition = "bottom",
222
230
  startAtZero = false,
223
- valueFormatter
231
+ valueFormatter,
232
+ tooltipFormatter
224
233
  } = options;
225
234
  const labelled = valueLabels ?? series.length <= RM_LINE_LABEL_MAX_SERIES;
226
235
  const AREA_MAX_SERIES = 10;
@@ -228,7 +237,7 @@ export function rmLineOption(categories, series, theme, palette, options = {}) {
228
237
  const dual = series.some((s) => s.axis === 1);
229
238
  const bottomLegend = legendPosition === "bottom";
230
239
  const format = (value) => valueFormatter ? valueFormatter(value) : String(value);
231
- const tooltipFormat = (value) => valueFormatter ? rmNumber(value) : String(value);
240
+ const tooltipFormat = (value) => tooltipFormatter ? tooltipFormatter(value) : valueFormatter ? rmNumber(value) : String(value);
232
241
  const values = series.flatMap((s) => s.data).filter((v) => typeof v === "number");
233
242
  const floorAtZero = startAtZero && values.length > 0 && Math.min(...values) >= 0;
234
243
  const base = rmBaseOption(theme, decal);
@@ -241,7 +250,7 @@ export function rmLineOption(categories, series, theme, palette, options = {}) {
241
250
  tooltip: {
242
251
  ...base.tooltip,
243
252
  trigger: rmTooltipTrigger(series.length),
244
- ...valueFormatter ? { valueFormatter: (value) => typeof value === "number" ? tooltipFormat(value) : "" } : {}
253
+ ...valueFormatter || tooltipFormatter ? { valueFormatter: (value) => typeof value === "number" ? tooltipFormat(value) : "" } : {}
245
254
  },
246
255
  // Legende obligatoire en double axe : sans elle, rien ne dit quelle courbe
247
256
  // se lit sur quel axe.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tekkare/romulus",
3
- "version": "0.1.205",
3
+ "version": "0.1.207",
4
4
  "description": "Romulus Design System - Nuxt module by Tekkare",
5
5
  "license": "MIT",
6
6
  "type": "module",