@tekkare/romulus 0.1.7 → 0.1.9
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.d.mts +19 -1
- package/dist/module.d.ts +19 -1
- package/dist/module.json +1 -1
- package/dist/module.mjs +7 -3
- package/dist/runtime/assets/styles/utilities.css +1 -1
- package/dist/runtime/components/BarChart.vue +45 -0
- package/dist/runtime/components/Chart.vue +75 -0
- package/dist/runtime/components/ChartCard.vue +5 -1
- package/dist/runtime/components/DonutChart.vue +27 -0
- package/dist/runtime/components/LineChart.vue +42 -0
- package/dist/runtime/components/SparklineChart.vue +33 -0
- package/dist/runtime/composables/useChartTheme.d.ts +7 -0
- package/dist/runtime/composables/useChartTheme.js +27 -0
- package/dist/runtime/utils/chart.d.ts +475 -0
- package/dist/runtime/utils/chart.js +209 -0
- package/package.json +3 -2
package/dist/module.d.mts
CHANGED
|
@@ -3,9 +3,27 @@ import * as _nuxt_schema from '@nuxt/schema';
|
|
|
3
3
|
interface RomulusModuleOptions {
|
|
4
4
|
/**
|
|
5
5
|
* Prefix for all Romulus components.
|
|
6
|
-
* @default '
|
|
6
|
+
* @default 'Rm'
|
|
7
7
|
*/
|
|
8
8
|
prefix?: string;
|
|
9
|
+
/**
|
|
10
|
+
* Injecte `base.css`, un reset global (`* { margin: 0 }`, `button { border: none }`,
|
|
11
|
+
* `a { text-decoration: none }`, couleur et fond sur `html`/`body`).
|
|
12
|
+
*
|
|
13
|
+
* A passer a `false` dans une app qui cohabite avec un autre socle CSS
|
|
14
|
+
* (Auriga, une feuille maison) : le reset y repeindrait toutes les pages
|
|
15
|
+
* existantes, pas seulement celles qui utilisent Romulus.
|
|
16
|
+
* @default true
|
|
17
|
+
*/
|
|
18
|
+
baseStyles?: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Injecte `utilities.css`. Les classes ne sont pas prefixees (`.row`,
|
|
21
|
+
* `.column`, `.block`, `.border`, `.absolute`...) et entrent donc en
|
|
22
|
+
* collision avec le CSS d'une app existante. A passer a `false` en
|
|
23
|
+
* cohabitation ; les tokens et les composants restent disponibles.
|
|
24
|
+
* @default true
|
|
25
|
+
*/
|
|
26
|
+
utilities?: boolean;
|
|
9
27
|
}
|
|
10
28
|
declare const _default: _nuxt_schema.NuxtModule<RomulusModuleOptions, RomulusModuleOptions, false>;
|
|
11
29
|
|
package/dist/module.d.ts
CHANGED
|
@@ -3,9 +3,27 @@ import * as _nuxt_schema from '@nuxt/schema';
|
|
|
3
3
|
interface RomulusModuleOptions {
|
|
4
4
|
/**
|
|
5
5
|
* Prefix for all Romulus components.
|
|
6
|
-
* @default '
|
|
6
|
+
* @default 'Rm'
|
|
7
7
|
*/
|
|
8
8
|
prefix?: string;
|
|
9
|
+
/**
|
|
10
|
+
* Injecte `base.css`, un reset global (`* { margin: 0 }`, `button { border: none }`,
|
|
11
|
+
* `a { text-decoration: none }`, couleur et fond sur `html`/`body`).
|
|
12
|
+
*
|
|
13
|
+
* A passer a `false` dans une app qui cohabite avec un autre socle CSS
|
|
14
|
+
* (Auriga, une feuille maison) : le reset y repeindrait toutes les pages
|
|
15
|
+
* existantes, pas seulement celles qui utilisent Romulus.
|
|
16
|
+
* @default true
|
|
17
|
+
*/
|
|
18
|
+
baseStyles?: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Injecte `utilities.css`. Les classes ne sont pas prefixees (`.row`,
|
|
21
|
+
* `.column`, `.block`, `.border`, `.absolute`...) et entrent donc en
|
|
22
|
+
* collision avec le CSS d'une app existante. A passer a `false` en
|
|
23
|
+
* cohabitation ; les tokens et les composants restent disponibles.
|
|
24
|
+
* @default true
|
|
25
|
+
*/
|
|
26
|
+
utilities?: boolean;
|
|
9
27
|
}
|
|
10
28
|
declare const _default: _nuxt_schema.NuxtModule<RomulusModuleOptions, RomulusModuleOptions, false>;
|
|
11
29
|
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -10,15 +10,19 @@ const module = defineNuxtModule({
|
|
|
10
10
|
}
|
|
11
11
|
},
|
|
12
12
|
defaults: {
|
|
13
|
-
prefix: "Rm"
|
|
13
|
+
prefix: "Rm",
|
|
14
|
+
baseStyles: true,
|
|
15
|
+
utilities: true
|
|
14
16
|
},
|
|
15
17
|
async setup(options, nuxt) {
|
|
16
18
|
const { resolve } = createResolver(import.meta.url);
|
|
17
19
|
const runtimeDir = fileURLToPath(new URL("./runtime", import.meta.url));
|
|
18
20
|
nuxt.options.css.push(resolve(runtimeDir, "assets/styles/tokens.css"));
|
|
19
|
-
|
|
21
|
+
if (options.baseStyles)
|
|
22
|
+
nuxt.options.css.push(resolve(runtimeDir, "assets/styles/base.css"));
|
|
20
23
|
nuxt.options.css.push(resolve(runtimeDir, "assets/styles/components.css"));
|
|
21
|
-
|
|
24
|
+
if (options.utilities)
|
|
25
|
+
nuxt.options.css.push(resolve(runtimeDir, "assets/styles/utilities.css"));
|
|
22
26
|
await addComponentsDir({
|
|
23
27
|
path: resolve(runtimeDir, "components"),
|
|
24
28
|
prefix: options.prefix
|
|
@@ -1 +1 @@
|
|
|
1
|
-
.row{display:flex;flex-direction:row}.column{display:flex;flex-direction:column}.row-reverse{display:flex;flex-direction:row-reverse}.column-reverse{display:flex;flex-direction:column-reverse}.x-start{justify-content:flex-start}.x-center{justify-content:center}.x-end{justify-content:flex-end}.x-between{justify-content:space-between}.x-around{justify-content:space-around}.x-evenly{justify-content:space-evenly}.y-start{align-items:flex-start}.y-center{align-items:center}.y-end{align-items:flex-end}.y-between{align-content:space-between}.y-stretch{align-items:stretch}.y-baseline{align-items:baseline}.wrap{flex-wrap:wrap}.nowrap{flex-wrap:nowrap}.grow{flex-grow:1}.grow-0{flex-grow:0}.shrink{flex-shrink:1}.shrink-0{flex-shrink:0}.flex-1{flex:1}.flex-auto{flex:auto}.flex-none{flex:none}.self-start{align-self:flex-start}.self-center{align-self:center}.self-end{align-self:flex-end}.self-stretch{align-self:stretch}.w-full{width:100%}.w-auto{width:auto}.w-fit{width:-moz-fit-content;width:fit-content}.min-w-0{min-width:0}.h-full{height:100%}.h-auto{height:auto}.h-screen{height:100vh}.min-h-0{min-height:0}.min-h-screen{min-height:100vh}.overflow-hidden{overflow:hidden}.overflow-auto{overflow:auto}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.relative{position:relative}.absolute{position:absolute}.fixed{position:fixed}.sticky{position:sticky}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.text-ellipsis{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.block{display:block}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.grid{display:grid}.hidden{display:none}.gap-
|
|
1
|
+
.row{display:flex;flex-direction:row}.column{display:flex;flex-direction:column}.row-reverse{display:flex;flex-direction:row-reverse}.column-reverse{display:flex;flex-direction:column-reverse}.x-start{justify-content:flex-start}.x-center{justify-content:center}.x-end{justify-content:flex-end}.x-between{justify-content:space-between}.x-around{justify-content:space-around}.x-evenly{justify-content:space-evenly}.y-start{align-items:flex-start}.y-center{align-items:center}.y-end{align-items:flex-end}.y-between{align-content:space-between}.y-stretch{align-items:stretch}.y-baseline{align-items:baseline}.wrap{flex-wrap:wrap}.nowrap{flex-wrap:nowrap}.grow{flex-grow:1}.grow-0{flex-grow:0}.shrink{flex-shrink:1}.shrink-0{flex-shrink:0}.flex-1{flex:1}.flex-auto{flex:auto}.flex-none{flex:none}.self-start{align-self:flex-start}.self-center{align-self:center}.self-end{align-self:flex-end}.self-stretch{align-self:stretch}.w-full{width:100%}.w-auto{width:auto}.w-fit{width:-moz-fit-content;width:fit-content}.min-w-0{min-width:0}.h-full{height:100%}.h-auto{height:auto}.h-screen{height:100vh}.min-h-0{min-height:0}.min-h-screen{min-height:100vh}.overflow-hidden{overflow:hidden}.overflow-auto{overflow:auto}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.relative{position:relative}.absolute{position:absolute}.fixed{position:fixed}.sticky{position:sticky}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.text-ellipsis{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.block{display:block}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.grid{display:grid}.hidden{display:none}.gap-2{gap:var(--space-2)}.gap-4{gap:var(--space-4)}.gap-6{gap:var(--space-6)}.gap-8{gap:var(--space-8)}.gap-12{gap:var(--space-12)}.gap-16{gap:var(--space-16)}.gap-20{gap:var(--space-20)}.gap-24{gap:var(--space-24)}.gap-32{gap:var(--space-32)}.gap-40{gap:var(--space-40)}.gap-48{gap:var(--space-48)}.p-0{padding:0}.p-2{padding:var(--space-2)}.p-4{padding:var(--space-4)}.p-6{padding:var(--space-6)}.p-8{padding:var(--space-8)}.p-12{padding:var(--space-12)}.p-16{padding:var(--space-16)}.p-20{padding:var(--space-20)}.p-24{padding:var(--space-24)}.p-32{padding:var(--space-32)}.p-40{padding:var(--space-40)}.p-48{padding:var(--space-48)}.px-2{padding-left:var(--space-2);padding-right:var(--space-2)}.px-4{padding-left:var(--space-4);padding-right:var(--space-4)}.px-6{padding-left:var(--space-6);padding-right:var(--space-6)}.px-8{padding-left:var(--space-8);padding-right:var(--space-8)}.px-12{padding-left:var(--space-12);padding-right:var(--space-12)}.px-16{padding-left:var(--space-16);padding-right:var(--space-16)}.px-20{padding-left:var(--space-20);padding-right:var(--space-20)}.px-24{padding-left:var(--space-24);padding-right:var(--space-24)}.px-32{padding-left:var(--space-32);padding-right:var(--space-32)}.px-40{padding-left:var(--space-40);padding-right:var(--space-40)}.px-48{padding-left:var(--space-48);padding-right:var(--space-48)}.py-2{padding-bottom:var(--space-2);padding-top:var(--space-2)}.py-4{padding-bottom:var(--space-4);padding-top:var(--space-4)}.py-6{padding-bottom:var(--space-6);padding-top:var(--space-6)}.py-8{padding-bottom:var(--space-8);padding-top:var(--space-8)}.py-12{padding-bottom:var(--space-12);padding-top:var(--space-12)}.py-16{padding-bottom:var(--space-16);padding-top:var(--space-16)}.py-20{padding-bottom:var(--space-20);padding-top:var(--space-20)}.py-24{padding-bottom:var(--space-24);padding-top:var(--space-24)}.py-32{padding-bottom:var(--space-32);padding-top:var(--space-32)}.py-40{padding-bottom:var(--space-40);padding-top:var(--space-40)}.py-48{padding-bottom:var(--space-48);padding-top:var(--space-48)}.m-0{margin:0}.m-auto{margin:auto}.mx-auto{margin-right:auto}.ml-auto,.mx-auto{margin-left:auto}.mr-auto{margin-right:auto}.mt-auto{margin-top:auto}.mb-auto{margin-bottom:auto}.border{border:1px solid var(--border-default)}.border-t{border-top:1px solid var(--border-default)}.border-b{border-bottom:1px solid var(--border-default)}.border-l{border-left:1px solid var(--border-default)}.border-r{border-right:1px solid var(--border-default)}.border-none{border:none}.rounded-sm{border-radius:var(--radius-sm)}.rounded-md{border-radius:var(--radius-md)}.rounded-lg{border-radius:var(--radius-lg)}.rounded-xl{border-radius:var(--radius-xl)}.rounded-full{border-radius:var(--radius-pill)}.cursor-pointer{cursor:pointer}.cursor-default{cursor:default}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
RmBarChart - bar chart vertical (une ou plusieurs series, empilable) ou
|
|
3
|
+
horizontal. Option construite par utils/chart, palette et theme resolus par
|
|
4
|
+
useChartTheme, donc mode nuit et modes daltoniens suivis automatiquement.
|
|
5
|
+
-->
|
|
6
|
+
<script setup lang="ts">
|
|
7
|
+
import { computed } from 'vue'
|
|
8
|
+
import { useChartTheme } from '../composables/useChartTheme'
|
|
9
|
+
import { rmBarHorizontalOption, rmBarVerticalOption, type RmSeriesInput, RM_CHART_HEIGHTS } from '../utils/chart'
|
|
10
|
+
|
|
11
|
+
const props = withDefaults(defineProps<{
|
|
12
|
+
/** Categories de l'axe : annees, libelles d'indicateur. */
|
|
13
|
+
categories: (string | number)[]
|
|
14
|
+
series: RmSeriesInput[]
|
|
15
|
+
horizontal?: boolean
|
|
16
|
+
height?: keyof typeof RM_CHART_HEIGHTS | number
|
|
17
|
+
description?: string
|
|
18
|
+
/** Formatage de la valeur en bout de barre (horizontal uniquement). */
|
|
19
|
+
labelFormatter?: (value: number) => string
|
|
20
|
+
}>(), {
|
|
21
|
+
horizontal: false,
|
|
22
|
+
height: 'standard',
|
|
23
|
+
description: undefined,
|
|
24
|
+
labelFormatter: undefined,
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
const { theme, palette, decal } = useChartTheme()
|
|
28
|
+
|
|
29
|
+
const option = computed(() =>
|
|
30
|
+
props.horizontal
|
|
31
|
+
? rmBarHorizontalOption(
|
|
32
|
+
props.categories.map(String),
|
|
33
|
+
props.series[0]?.data ?? [],
|
|
34
|
+
theme.value,
|
|
35
|
+
palette.value.cat,
|
|
36
|
+
decal.value,
|
|
37
|
+
props.labelFormatter,
|
|
38
|
+
)
|
|
39
|
+
: rmBarVerticalOption(props.categories, props.series, theme.value, palette.value.cat, decal.value),
|
|
40
|
+
)
|
|
41
|
+
</script>
|
|
42
|
+
|
|
43
|
+
<template>
|
|
44
|
+
<RmChart :option="option" :height="height" :description="description" />
|
|
45
|
+
</template>
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
RmChart - enveloppe ECharts bas niveau. Les composants RmBarChart,
|
|
3
|
+
RmLineChart, RmDonutChart et RmSparklineChart construisent leur `option` et
|
|
4
|
+
la passent ici ; une page peut aussi fournir une option brute pour un type
|
|
5
|
+
non couvert.
|
|
6
|
+
|
|
7
|
+
Renderer SVG impose, comme dans le design system : net en HiDPI, et
|
|
8
|
+
exportable en vectoriel depuis le menu de la chart-card.
|
|
9
|
+
-->
|
|
10
|
+
<script setup lang="ts">
|
|
11
|
+
import { onBeforeUnmount, onMounted, ref, shallowRef, watch } from 'vue'
|
|
12
|
+
import { RM_CHART_HEIGHTS } from '../utils/chart'
|
|
13
|
+
|
|
14
|
+
const props = withDefaults(defineProps<{
|
|
15
|
+
/** Option ECharts complete. */
|
|
16
|
+
option: Record<string, unknown>
|
|
17
|
+
/** Hauteur : un palier normalise du design system, ou un nombre de pixels. */
|
|
18
|
+
height?: keyof typeof RM_CHART_HEIGHTS | number
|
|
19
|
+
/**
|
|
20
|
+
* Description textuelle du graphe pour les lecteurs d'ecran. ECharts genere
|
|
21
|
+
* un resume automatique, mais il enonce des series et des valeurs brutes :
|
|
22
|
+
* une phrase de lecture vaut mieux. Le survol n'etant jamais le seul acces
|
|
23
|
+
* a l'information, la donnee cle doit de toute facon rester dans le texte
|
|
24
|
+
* de l'ecran.
|
|
25
|
+
*/
|
|
26
|
+
description?: string
|
|
27
|
+
}>(), {
|
|
28
|
+
height: 'standard',
|
|
29
|
+
description: undefined,
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
const el = ref<HTMLElement | null>(null)
|
|
33
|
+
const chart = shallowRef<{ setOption: (o: unknown, n?: boolean) => void, resize: () => void, dispose: () => void } | null>(null)
|
|
34
|
+
let observer: ResizeObserver | null = null
|
|
35
|
+
|
|
36
|
+
const heightPx = () =>
|
|
37
|
+
typeof props.height === 'number' ? props.height : RM_CHART_HEIGHTS[props.height]
|
|
38
|
+
|
|
39
|
+
onMounted(async () => {
|
|
40
|
+
if (!el.value) return
|
|
41
|
+
const echarts = await import('echarts')
|
|
42
|
+
chart.value = echarts.init(el.value, undefined, { renderer: 'svg' })
|
|
43
|
+
chart.value.setOption(props.option)
|
|
44
|
+
|
|
45
|
+
// ResizeObserver plutot qu'un listener global sur window : un graphe dans un
|
|
46
|
+
// panneau repliable ou une grille change de taille sans que la fenetre bouge.
|
|
47
|
+
observer = new ResizeObserver(() => chart.value?.resize())
|
|
48
|
+
observer.observe(el.value)
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
// `true` en second argument : on remplace l'option au lieu de la fusionner.
|
|
52
|
+
// Sans ca, retirer une serie la laisse affichee, parce qu'ECharts fusionne
|
|
53
|
+
// par defaut et ne sait pas qu'elle a disparu.
|
|
54
|
+
watch(() => props.option, (next) => chart.value?.setOption(next, true), { deep: true })
|
|
55
|
+
|
|
56
|
+
onBeforeUnmount(() => {
|
|
57
|
+
observer?.disconnect()
|
|
58
|
+
observer = null
|
|
59
|
+
chart.value?.dispose()
|
|
60
|
+
chart.value = null
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
defineExpose({ instance: chart })
|
|
64
|
+
</script>
|
|
65
|
+
|
|
66
|
+
<template>
|
|
67
|
+
<figure class="rm-chart">
|
|
68
|
+
<div ref="el" class="echart" :style="{ height: `${heightPx()}px` }" role="img" :aria-label="description" />
|
|
69
|
+
<figcaption v-if="description" class="sr-only">{{ description }}</figcaption>
|
|
70
|
+
</figure>
|
|
71
|
+
</template>
|
|
72
|
+
|
|
73
|
+
<style scoped>
|
|
74
|
+
.rm-chart{margin:0}.echart{width:100%}
|
|
75
|
+
</style>
|
|
@@ -34,6 +34,7 @@ const props = withDefaults(defineProps<{
|
|
|
34
34
|
|
|
35
35
|
const emit = defineEmits<{
|
|
36
36
|
exportPng: []
|
|
37
|
+
exportSvg: []
|
|
37
38
|
exportCsv: []
|
|
38
39
|
exportXlsx: []
|
|
39
40
|
copyLink: []
|
|
@@ -67,7 +68,7 @@ onBeforeUnmount(() => {
|
|
|
67
68
|
document.removeEventListener('keydown', onEscape)
|
|
68
69
|
})
|
|
69
70
|
|
|
70
|
-
function run(action: 'exportPng' | 'exportCsv' | 'exportXlsx' | 'copyLink' | 'fullscreen') {
|
|
71
|
+
function run(action: 'exportPng' | 'exportSvg' | 'exportCsv' | 'exportXlsx' | 'copyLink' | 'fullscreen') {
|
|
71
72
|
menuOpen.value = false
|
|
72
73
|
emit(action)
|
|
73
74
|
}
|
|
@@ -94,6 +95,9 @@ function run(action: 'exportPng' | 'exportCsv' | 'exportXlsx' | 'copyLink' | 'fu
|
|
|
94
95
|
<button class="cc-menu-item" type="button" role="menuitem" @click="run('exportPng')">
|
|
95
96
|
<RmIcon name="Image" :size="14" /> Image PNG
|
|
96
97
|
</button>
|
|
98
|
+
<button class="cc-menu-item" type="button" role="menuitem" @click="run('exportSvg')">
|
|
99
|
+
<RmIcon name="VectorThree" :size="14" /> Vectoriel SVG
|
|
100
|
+
</button>
|
|
97
101
|
<button class="cc-menu-item" type="button" role="menuitem" @click="run('exportCsv')">
|
|
98
102
|
<RmIcon name="FileCsv" :size="14" /> Données CSV
|
|
99
103
|
</button>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
RmDonutChart - repartition en parts. Au-dela de six parts, regrouper le
|
|
3
|
+
reste en "Autres" : la palette categorielle s'arrete a six couleurs
|
|
4
|
+
distinguables sous dyschromatopsie.
|
|
5
|
+
-->
|
|
6
|
+
<script setup lang="ts">
|
|
7
|
+
import { computed } from 'vue'
|
|
8
|
+
import { useChartTheme } from '../composables/useChartTheme'
|
|
9
|
+
import { rmDonutOption, RM_CHART_HEIGHTS } from '../utils/chart'
|
|
10
|
+
|
|
11
|
+
const props = withDefaults(defineProps<{
|
|
12
|
+
data: { name: string, value: number }[]
|
|
13
|
+
height?: keyof typeof RM_CHART_HEIGHTS | number
|
|
14
|
+
description?: string
|
|
15
|
+
}>(), {
|
|
16
|
+
height: 'standard',
|
|
17
|
+
description: undefined,
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
const { theme, palette, decal } = useChartTheme()
|
|
21
|
+
|
|
22
|
+
const option = computed(() => rmDonutOption(props.data, theme.value, palette.value.cat, decal.value))
|
|
23
|
+
</script>
|
|
24
|
+
|
|
25
|
+
<template>
|
|
26
|
+
<RmChart :option="option" :height="height" :description="description" />
|
|
27
|
+
</template>
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
RmLineChart - courbe, aire, ou marche d'escalier (`step`) pour les series de
|
|
3
|
+
prix et de decisions discretes.
|
|
4
|
+
-->
|
|
5
|
+
<script setup lang="ts">
|
|
6
|
+
import { computed } from 'vue'
|
|
7
|
+
import { useChartTheme } from '../composables/useChartTheme'
|
|
8
|
+
import { rmLineOption, type RmSeriesInput, RM_CHART_HEIGHTS } from '../utils/chart'
|
|
9
|
+
|
|
10
|
+
const props = withDefaults(defineProps<{
|
|
11
|
+
categories: (string | number)[]
|
|
12
|
+
series: RmSeriesInput[]
|
|
13
|
+
/** Remplissage degrade sous la courbe. */
|
|
14
|
+
area?: boolean
|
|
15
|
+
/** Marche d'escalier : prix, taux de remboursement, decisions datees. */
|
|
16
|
+
step?: boolean
|
|
17
|
+
smooth?: boolean
|
|
18
|
+
height?: keyof typeof RM_CHART_HEIGHTS | number
|
|
19
|
+
description?: string
|
|
20
|
+
}>(), {
|
|
21
|
+
area: false,
|
|
22
|
+
step: false,
|
|
23
|
+
smooth: true,
|
|
24
|
+
height: 'standard',
|
|
25
|
+
description: undefined,
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
const { theme, palette, decal } = useChartTheme()
|
|
29
|
+
|
|
30
|
+
const option = computed(() =>
|
|
31
|
+
rmLineOption(props.categories, props.series, theme.value, palette.value.cat, {
|
|
32
|
+
area: props.area,
|
|
33
|
+
step: props.step,
|
|
34
|
+
smooth: props.smooth,
|
|
35
|
+
decal: decal.value,
|
|
36
|
+
}),
|
|
37
|
+
)
|
|
38
|
+
</script>
|
|
39
|
+
|
|
40
|
+
<template>
|
|
41
|
+
<RmChart :option="option" :height="height" :description="description" />
|
|
42
|
+
</template>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
RmSparklineChart - micro-courbe pour une cellule de tableau ou une tuile KPI.
|
|
3
|
+
Sans axes ni tooltip : elle donne une forme, pas une valeur. La valeur doit
|
|
4
|
+
rester lisible en texte a cote, le survol n'etant jamais le seul acces a
|
|
5
|
+
l'information.
|
|
6
|
+
-->
|
|
7
|
+
<script setup lang="ts">
|
|
8
|
+
import { computed } from 'vue'
|
|
9
|
+
import { useChartTheme } from '../composables/useChartTheme'
|
|
10
|
+
import { rmSparklineOption, RM_CHART_HEIGHTS } from '../utils/chart'
|
|
11
|
+
|
|
12
|
+
const props = withDefaults(defineProps<{
|
|
13
|
+
values: (number | null)[]
|
|
14
|
+
/** Couleur explicite ; par defaut la primaire de la palette active. */
|
|
15
|
+
color?: string
|
|
16
|
+
height?: keyof typeof RM_CHART_HEIGHTS | number
|
|
17
|
+
description?: string
|
|
18
|
+
}>(), {
|
|
19
|
+
color: undefined,
|
|
20
|
+
height: 'sparkline',
|
|
21
|
+
description: undefined,
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
// Pas de `theme` ici : une sparkline n'a ni axe ni tooltip a colorer. Le
|
|
25
|
+
// basculement jour/nuit la recalcule quand meme, parce que `palette` en depend.
|
|
26
|
+
const { palette } = useChartTheme()
|
|
27
|
+
|
|
28
|
+
const option = computed(() => rmSparklineOption(props.values, props.color ?? palette.value.primary))
|
|
29
|
+
</script>
|
|
30
|
+
|
|
31
|
+
<template>
|
|
32
|
+
<RmChart :option="option" :height="height" :description="description" />
|
|
33
|
+
</template>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare function useChartTheme(): {
|
|
2
|
+
night: import("vue").Ref<boolean, boolean>;
|
|
3
|
+
cvd: import("vue").Ref<boolean, boolean>;
|
|
4
|
+
theme: import("vue").ComputedRef<import("../utils/chart").RmChartTheme>;
|
|
5
|
+
palette: import("vue").ComputedRef<import("../utils/chart").RmPalette>;
|
|
6
|
+
decal: import("vue").ComputedRef<boolean>;
|
|
7
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { computed, onBeforeUnmount, onMounted, ref } from "vue";
|
|
2
|
+
import { rmChartTheme, rmPalette } from "../utils/chart.js";
|
|
3
|
+
const CVD_CLASSES = ["cvd-deutera", "cvd-protan", "cvd-trita", "cvd-mono"];
|
|
4
|
+
export function useChartTheme() {
|
|
5
|
+
const night = ref(false);
|
|
6
|
+
const cvd = ref(false);
|
|
7
|
+
let observer = null;
|
|
8
|
+
function read() {
|
|
9
|
+
if (typeof document === "undefined") return;
|
|
10
|
+
const list = document.body.classList;
|
|
11
|
+
night.value = list.contains("night");
|
|
12
|
+
cvd.value = CVD_CLASSES.some((c) => list.contains(c));
|
|
13
|
+
}
|
|
14
|
+
onMounted(() => {
|
|
15
|
+
read();
|
|
16
|
+
observer = new MutationObserver(read);
|
|
17
|
+
observer.observe(document.body, { attributes: true, attributeFilter: ["class"] });
|
|
18
|
+
});
|
|
19
|
+
onBeforeUnmount(() => {
|
|
20
|
+
observer?.disconnect();
|
|
21
|
+
observer = null;
|
|
22
|
+
});
|
|
23
|
+
const theme = computed(() => rmChartTheme(night.value));
|
|
24
|
+
const palette = computed(() => rmPalette(cvd.value ? "cvdSafe" : "default", night.value));
|
|
25
|
+
const decal = computed(() => cvd.value);
|
|
26
|
+
return { night, cvd, theme, palette, decal };
|
|
27
|
+
}
|
|
@@ -0,0 +1,475 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Theme et constructeurs d'option ECharts.
|
|
3
|
+
*
|
|
4
|
+
* Porte depuis le design system : palettes et resolveur de `reference.html`
|
|
5
|
+
* (`window.OIP_PALETTES` / `CHART_THEME`), constructeurs d'option de
|
|
6
|
+
* `references/charts.md`. ECharts v5 est la librairie standard de tous les
|
|
7
|
+
* dashboards Tekkare ; ne pas en introduire une seconde.
|
|
8
|
+
*
|
|
9
|
+
* Les couleurs sont ici en litteral et non en `var(--...)` : ECharts peint
|
|
10
|
+
* dans un canvas SVG qu'il genere lui-meme, il ne resout pas les variables
|
|
11
|
+
* CSS. C'est la seule raison acceptee d'ecrire un hex hors tokens.
|
|
12
|
+
*/
|
|
13
|
+
export interface RmPalette {
|
|
14
|
+
/** Palette categorielle, 6 couleurs maximum sans ordre. Au-dela, passer en sequentiel ou regrouper en "Autres". */
|
|
15
|
+
cat: string[];
|
|
16
|
+
primary: string;
|
|
17
|
+
accent: string;
|
|
18
|
+
neutral: string;
|
|
19
|
+
/** Palette sequentielle, du plus clair au plus fonce : heatmaps, choroplethes. */
|
|
20
|
+
seq: string[];
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Les quatre palettes du design system. `cvdSafe` est la palette Okabe-Ito,
|
|
24
|
+
* validee pour les trois principaux types de dyschromatopsie ; le violet OIP
|
|
25
|
+
* y devient le bleu #0072B2 qui joue le role de couleur primaire.
|
|
26
|
+
*/
|
|
27
|
+
export declare const RM_PALETTES: Record<string, RmPalette>;
|
|
28
|
+
/** Palettes par verticale produit, cf. references/charts.md. */
|
|
29
|
+
export declare const RM_PRODUCT_PALETTES: Record<string, string[]>;
|
|
30
|
+
/** Palette divergente : negatif vers neutre vers positif. */
|
|
31
|
+
export declare const RM_DIVERGING: string[];
|
|
32
|
+
export interface RmChartTheme {
|
|
33
|
+
night: boolean;
|
|
34
|
+
textColor: string;
|
|
35
|
+
mutedColor: string;
|
|
36
|
+
dimmedColor: string;
|
|
37
|
+
splitLine: string;
|
|
38
|
+
axisLine: string;
|
|
39
|
+
tooltipBg: string;
|
|
40
|
+
tooltipText: string;
|
|
41
|
+
tooltipBorder: string;
|
|
42
|
+
backgroundColor: string;
|
|
43
|
+
}
|
|
44
|
+
export declare const rmChartTheme: (night: boolean) => RmChartTheme;
|
|
45
|
+
/**
|
|
46
|
+
* Resout la palette active. En mode nuit, la variante *Dark prend le relais
|
|
47
|
+
* quand elle existe : les teintes du mode jour ne contrastent pas assez sur
|
|
48
|
+
* #0E0E14 et une serie devient illisible, ce qui est un bug d'accessibilite
|
|
49
|
+
* et pas un choix esthetique.
|
|
50
|
+
*/
|
|
51
|
+
export declare function rmPalette(name?: string, night?: boolean): RmPalette;
|
|
52
|
+
/** Hauteurs normalisees du design system. */
|
|
53
|
+
export declare const RM_CHART_HEIGHTS: {
|
|
54
|
+
readonly sparkline: 48;
|
|
55
|
+
readonly kpi: 80;
|
|
56
|
+
readonly compact: 200;
|
|
57
|
+
readonly standard: 260;
|
|
58
|
+
readonly large: 320;
|
|
59
|
+
readonly full: 360;
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Socle commun a tous les graphes. `decal` active le double encodage par
|
|
63
|
+
* motifs : sur un graphe multi-series, la couleur seule ne suffit pas, c'est
|
|
64
|
+
* une regle du design system et non une option.
|
|
65
|
+
*/
|
|
66
|
+
export declare function rmBaseOption(theme: RmChartTheme, decal?: boolean): {
|
|
67
|
+
backgroundColor: string;
|
|
68
|
+
animation: boolean;
|
|
69
|
+
aria: {
|
|
70
|
+
enabled: boolean;
|
|
71
|
+
decal: {
|
|
72
|
+
show: boolean;
|
|
73
|
+
};
|
|
74
|
+
};
|
|
75
|
+
textStyle: {
|
|
76
|
+
fontFamily: string;
|
|
77
|
+
};
|
|
78
|
+
grid: {
|
|
79
|
+
left: number;
|
|
80
|
+
right: number;
|
|
81
|
+
top: number;
|
|
82
|
+
bottom: number;
|
|
83
|
+
containLabel: boolean;
|
|
84
|
+
};
|
|
85
|
+
tooltip: {
|
|
86
|
+
trigger: string;
|
|
87
|
+
backgroundColor: string;
|
|
88
|
+
textStyle: {
|
|
89
|
+
color: string;
|
|
90
|
+
fontFamily: string;
|
|
91
|
+
fontSize: number;
|
|
92
|
+
};
|
|
93
|
+
borderColor: string;
|
|
94
|
+
borderWidth: number;
|
|
95
|
+
};
|
|
96
|
+
};
|
|
97
|
+
export interface RmSeriesInput {
|
|
98
|
+
name?: string;
|
|
99
|
+
data: (number | null)[];
|
|
100
|
+
color?: string;
|
|
101
|
+
/** Cle d'empilement : deux series avec la meme cle s'empilent. */
|
|
102
|
+
stack?: string;
|
|
103
|
+
}
|
|
104
|
+
/** Bar chart vertical, une ou plusieurs series, empilable. */
|
|
105
|
+
export declare function rmBarVerticalOption(categories: (string | number)[], series: RmSeriesInput[], theme: RmChartTheme, palette: string[], decal?: boolean): {
|
|
106
|
+
legend: {
|
|
107
|
+
top: number;
|
|
108
|
+
textStyle: {
|
|
109
|
+
color: string;
|
|
110
|
+
fontSize: number;
|
|
111
|
+
fontFamily: string;
|
|
112
|
+
};
|
|
113
|
+
} | undefined;
|
|
114
|
+
xAxis: {
|
|
115
|
+
splitLine: {
|
|
116
|
+
show: boolean;
|
|
117
|
+
};
|
|
118
|
+
axisLabel: {
|
|
119
|
+
fontSize: number;
|
|
120
|
+
color: string;
|
|
121
|
+
fontFamily: string;
|
|
122
|
+
};
|
|
123
|
+
axisLine: {
|
|
124
|
+
lineStyle: {
|
|
125
|
+
color: string;
|
|
126
|
+
};
|
|
127
|
+
};
|
|
128
|
+
type: string;
|
|
129
|
+
data: (string | number)[];
|
|
130
|
+
};
|
|
131
|
+
yAxis: {
|
|
132
|
+
axisLabel: {
|
|
133
|
+
fontSize: number;
|
|
134
|
+
color: string;
|
|
135
|
+
fontFamily: string;
|
|
136
|
+
};
|
|
137
|
+
axisLine: {
|
|
138
|
+
lineStyle: {
|
|
139
|
+
color: string;
|
|
140
|
+
};
|
|
141
|
+
};
|
|
142
|
+
splitLine: {
|
|
143
|
+
lineStyle: {
|
|
144
|
+
color: string;
|
|
145
|
+
};
|
|
146
|
+
};
|
|
147
|
+
type: string;
|
|
148
|
+
};
|
|
149
|
+
series: {
|
|
150
|
+
type: string;
|
|
151
|
+
name: string | undefined;
|
|
152
|
+
stack: string | undefined;
|
|
153
|
+
data: (number | null)[];
|
|
154
|
+
itemStyle: {
|
|
155
|
+
color: string;
|
|
156
|
+
borderRadius: number[];
|
|
157
|
+
};
|
|
158
|
+
}[];
|
|
159
|
+
backgroundColor: string;
|
|
160
|
+
animation: boolean;
|
|
161
|
+
aria: {
|
|
162
|
+
enabled: boolean;
|
|
163
|
+
decal: {
|
|
164
|
+
show: boolean;
|
|
165
|
+
};
|
|
166
|
+
};
|
|
167
|
+
textStyle: {
|
|
168
|
+
fontFamily: string;
|
|
169
|
+
};
|
|
170
|
+
grid: {
|
|
171
|
+
left: number;
|
|
172
|
+
right: number;
|
|
173
|
+
top: number;
|
|
174
|
+
bottom: number;
|
|
175
|
+
containLabel: boolean;
|
|
176
|
+
};
|
|
177
|
+
tooltip: {
|
|
178
|
+
trigger: string;
|
|
179
|
+
backgroundColor: string;
|
|
180
|
+
textStyle: {
|
|
181
|
+
color: string;
|
|
182
|
+
fontFamily: string;
|
|
183
|
+
fontSize: number;
|
|
184
|
+
};
|
|
185
|
+
borderColor: string;
|
|
186
|
+
borderWidth: number;
|
|
187
|
+
};
|
|
188
|
+
};
|
|
189
|
+
/** Bar chart horizontal, avec valeur affichee en bout de barre. */
|
|
190
|
+
export declare function rmBarHorizontalOption(labels: string[], values: (number | null)[], theme: RmChartTheme, palette: string[], decal?: boolean, formatter?: (value: number) => string): {
|
|
191
|
+
grid: {
|
|
192
|
+
left: number;
|
|
193
|
+
right: number;
|
|
194
|
+
top: number;
|
|
195
|
+
bottom: number;
|
|
196
|
+
containLabel: boolean;
|
|
197
|
+
};
|
|
198
|
+
xAxis: {
|
|
199
|
+
axisLabel: {
|
|
200
|
+
fontSize: number;
|
|
201
|
+
color: string;
|
|
202
|
+
fontFamily: string;
|
|
203
|
+
};
|
|
204
|
+
axisLine: {
|
|
205
|
+
lineStyle: {
|
|
206
|
+
color: string;
|
|
207
|
+
};
|
|
208
|
+
};
|
|
209
|
+
splitLine: {
|
|
210
|
+
lineStyle: {
|
|
211
|
+
color: string;
|
|
212
|
+
};
|
|
213
|
+
};
|
|
214
|
+
type: string;
|
|
215
|
+
};
|
|
216
|
+
yAxis: {
|
|
217
|
+
splitLine: {
|
|
218
|
+
show: boolean;
|
|
219
|
+
};
|
|
220
|
+
axisLabel: {
|
|
221
|
+
fontSize: number;
|
|
222
|
+
color: string;
|
|
223
|
+
fontFamily: string;
|
|
224
|
+
};
|
|
225
|
+
axisLine: {
|
|
226
|
+
lineStyle: {
|
|
227
|
+
color: string;
|
|
228
|
+
};
|
|
229
|
+
};
|
|
230
|
+
type: string;
|
|
231
|
+
data: string[];
|
|
232
|
+
};
|
|
233
|
+
series: {
|
|
234
|
+
type: string;
|
|
235
|
+
data: (number | null)[];
|
|
236
|
+
itemStyle: {
|
|
237
|
+
color: (params: {
|
|
238
|
+
dataIndex: number;
|
|
239
|
+
}) => string;
|
|
240
|
+
borderRadius: number[];
|
|
241
|
+
};
|
|
242
|
+
label: {
|
|
243
|
+
show: boolean;
|
|
244
|
+
position: string;
|
|
245
|
+
fontSize: number;
|
|
246
|
+
color: string;
|
|
247
|
+
fontFamily: string;
|
|
248
|
+
formatter: ((p: {
|
|
249
|
+
value: number;
|
|
250
|
+
}) => string) | undefined;
|
|
251
|
+
};
|
|
252
|
+
}[];
|
|
253
|
+
backgroundColor: string;
|
|
254
|
+
animation: boolean;
|
|
255
|
+
aria: {
|
|
256
|
+
enabled: boolean;
|
|
257
|
+
decal: {
|
|
258
|
+
show: boolean;
|
|
259
|
+
};
|
|
260
|
+
};
|
|
261
|
+
textStyle: {
|
|
262
|
+
fontFamily: string;
|
|
263
|
+
};
|
|
264
|
+
tooltip: {
|
|
265
|
+
trigger: string;
|
|
266
|
+
backgroundColor: string;
|
|
267
|
+
textStyle: {
|
|
268
|
+
color: string;
|
|
269
|
+
fontFamily: string;
|
|
270
|
+
fontSize: number;
|
|
271
|
+
};
|
|
272
|
+
borderColor: string;
|
|
273
|
+
borderWidth: number;
|
|
274
|
+
};
|
|
275
|
+
};
|
|
276
|
+
/** Line ou area chart. `step` produit la marche d'escalier des series de prix. */
|
|
277
|
+
export declare function rmLineOption(categories: (string | number)[], series: RmSeriesInput[], theme: RmChartTheme, palette: string[], options?: {
|
|
278
|
+
area?: boolean;
|
|
279
|
+
step?: boolean;
|
|
280
|
+
smooth?: boolean;
|
|
281
|
+
decal?: boolean;
|
|
282
|
+
}): {
|
|
283
|
+
legend: {
|
|
284
|
+
top: number;
|
|
285
|
+
textStyle: {
|
|
286
|
+
color: string;
|
|
287
|
+
fontSize: number;
|
|
288
|
+
fontFamily: string;
|
|
289
|
+
};
|
|
290
|
+
} | undefined;
|
|
291
|
+
xAxis: {
|
|
292
|
+
splitLine: {
|
|
293
|
+
show: boolean;
|
|
294
|
+
};
|
|
295
|
+
boundaryGap: boolean;
|
|
296
|
+
axisLabel: {
|
|
297
|
+
fontSize: number;
|
|
298
|
+
color: string;
|
|
299
|
+
fontFamily: string;
|
|
300
|
+
};
|
|
301
|
+
axisLine: {
|
|
302
|
+
lineStyle: {
|
|
303
|
+
color: string;
|
|
304
|
+
};
|
|
305
|
+
};
|
|
306
|
+
type: string;
|
|
307
|
+
data: (string | number)[];
|
|
308
|
+
};
|
|
309
|
+
yAxis: {
|
|
310
|
+
axisLabel: {
|
|
311
|
+
fontSize: number;
|
|
312
|
+
color: string;
|
|
313
|
+
fontFamily: string;
|
|
314
|
+
};
|
|
315
|
+
axisLine: {
|
|
316
|
+
lineStyle: {
|
|
317
|
+
color: string;
|
|
318
|
+
};
|
|
319
|
+
};
|
|
320
|
+
splitLine: {
|
|
321
|
+
lineStyle: {
|
|
322
|
+
color: string;
|
|
323
|
+
};
|
|
324
|
+
};
|
|
325
|
+
type: string;
|
|
326
|
+
};
|
|
327
|
+
series: {
|
|
328
|
+
type: string;
|
|
329
|
+
name: string | undefined;
|
|
330
|
+
data: (number | null)[];
|
|
331
|
+
smooth: boolean;
|
|
332
|
+
step: string | undefined;
|
|
333
|
+
symbol: string;
|
|
334
|
+
symbolSize: number;
|
|
335
|
+
lineStyle: {
|
|
336
|
+
color: string;
|
|
337
|
+
width: number;
|
|
338
|
+
};
|
|
339
|
+
itemStyle: {
|
|
340
|
+
color: string;
|
|
341
|
+
};
|
|
342
|
+
areaStyle: {
|
|
343
|
+
color: {
|
|
344
|
+
type: string;
|
|
345
|
+
x: number;
|
|
346
|
+
y: number;
|
|
347
|
+
x2: number;
|
|
348
|
+
y2: number;
|
|
349
|
+
colorStops: {
|
|
350
|
+
offset: number;
|
|
351
|
+
color: string;
|
|
352
|
+
}[];
|
|
353
|
+
};
|
|
354
|
+
} | undefined;
|
|
355
|
+
}[];
|
|
356
|
+
backgroundColor: string;
|
|
357
|
+
animation: boolean;
|
|
358
|
+
aria: {
|
|
359
|
+
enabled: boolean;
|
|
360
|
+
decal: {
|
|
361
|
+
show: boolean;
|
|
362
|
+
};
|
|
363
|
+
};
|
|
364
|
+
textStyle: {
|
|
365
|
+
fontFamily: string;
|
|
366
|
+
};
|
|
367
|
+
grid: {
|
|
368
|
+
left: number;
|
|
369
|
+
right: number;
|
|
370
|
+
top: number;
|
|
371
|
+
bottom: number;
|
|
372
|
+
containLabel: boolean;
|
|
373
|
+
};
|
|
374
|
+
tooltip: {
|
|
375
|
+
trigger: string;
|
|
376
|
+
backgroundColor: string;
|
|
377
|
+
textStyle: {
|
|
378
|
+
color: string;
|
|
379
|
+
fontFamily: string;
|
|
380
|
+
fontSize: number;
|
|
381
|
+
};
|
|
382
|
+
borderColor: string;
|
|
383
|
+
borderWidth: number;
|
|
384
|
+
};
|
|
385
|
+
};
|
|
386
|
+
/** Donut. Au-dela de 6 parts, regrouper le reste en "Autres". */
|
|
387
|
+
export declare function rmDonutOption(data: {
|
|
388
|
+
name: string;
|
|
389
|
+
value: number;
|
|
390
|
+
}[], theme: RmChartTheme, palette: string[], decal?: boolean): {
|
|
391
|
+
grid: undefined;
|
|
392
|
+
tooltip: {
|
|
393
|
+
trigger: string;
|
|
394
|
+
backgroundColor: string;
|
|
395
|
+
textStyle: {
|
|
396
|
+
color: string;
|
|
397
|
+
fontFamily: string;
|
|
398
|
+
fontSize: number;
|
|
399
|
+
};
|
|
400
|
+
borderColor: string;
|
|
401
|
+
borderWidth: number;
|
|
402
|
+
};
|
|
403
|
+
legend: {
|
|
404
|
+
bottom: number;
|
|
405
|
+
textStyle: {
|
|
406
|
+
color: string;
|
|
407
|
+
fontSize: number;
|
|
408
|
+
fontFamily: string;
|
|
409
|
+
};
|
|
410
|
+
};
|
|
411
|
+
series: {
|
|
412
|
+
type: string;
|
|
413
|
+
radius: string[];
|
|
414
|
+
center: string[];
|
|
415
|
+
data: {
|
|
416
|
+
value: number;
|
|
417
|
+
name: string;
|
|
418
|
+
itemStyle: {
|
|
419
|
+
color: string;
|
|
420
|
+
};
|
|
421
|
+
}[];
|
|
422
|
+
label: {
|
|
423
|
+
fontSize: number;
|
|
424
|
+
color: string;
|
|
425
|
+
fontFamily: string;
|
|
426
|
+
};
|
|
427
|
+
}[];
|
|
428
|
+
backgroundColor: string;
|
|
429
|
+
animation: boolean;
|
|
430
|
+
aria: {
|
|
431
|
+
enabled: boolean;
|
|
432
|
+
decal: {
|
|
433
|
+
show: boolean;
|
|
434
|
+
};
|
|
435
|
+
};
|
|
436
|
+
textStyle: {
|
|
437
|
+
fontFamily: string;
|
|
438
|
+
};
|
|
439
|
+
};
|
|
440
|
+
/** Sparkline : pas d'axes, pas de tooltip, destinee a une cellule ou une tuile KPI. */
|
|
441
|
+
export declare function rmSparklineOption(values: (number | null)[], color: string): {
|
|
442
|
+
backgroundColor: string;
|
|
443
|
+
animation: boolean;
|
|
444
|
+
grid: {
|
|
445
|
+
left: number;
|
|
446
|
+
right: number;
|
|
447
|
+
top: number;
|
|
448
|
+
bottom: number;
|
|
449
|
+
};
|
|
450
|
+
xAxis: {
|
|
451
|
+
type: string;
|
|
452
|
+
show: boolean;
|
|
453
|
+
data: number[];
|
|
454
|
+
};
|
|
455
|
+
yAxis: {
|
|
456
|
+
type: string;
|
|
457
|
+
show: boolean;
|
|
458
|
+
};
|
|
459
|
+
tooltip: {
|
|
460
|
+
show: boolean;
|
|
461
|
+
};
|
|
462
|
+
series: {
|
|
463
|
+
type: string;
|
|
464
|
+
data: (number | null)[];
|
|
465
|
+
smooth: boolean;
|
|
466
|
+
symbol: string;
|
|
467
|
+
lineStyle: {
|
|
468
|
+
color: string;
|
|
469
|
+
width: number;
|
|
470
|
+
};
|
|
471
|
+
areaStyle: {
|
|
472
|
+
color: string;
|
|
473
|
+
};
|
|
474
|
+
}[];
|
|
475
|
+
};
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
export const RM_PALETTES = {
|
|
2
|
+
default: {
|
|
3
|
+
cat: ["#2100AD", "#00D4A8", "#F59E0B", "#4164EE", "#EC4899", "#6B3FFF"],
|
|
4
|
+
primary: "#2100AD",
|
|
5
|
+
accent: "#6B3FFF",
|
|
6
|
+
neutral: "#D6CCFF",
|
|
7
|
+
seq: ["#EEF1FE", "#D6CCFF", "#B485FF", "#8B5CF6", "#6B3FFF", "#2100AD"]
|
|
8
|
+
},
|
|
9
|
+
defaultDark: {
|
|
10
|
+
cat: ["#9B7CFF", "#3DD9B9", "#FBBF24", "#7AA0FF", "#F472B6", "#B485FF"],
|
|
11
|
+
primary: "#9B7CFF",
|
|
12
|
+
accent: "#B485FF",
|
|
13
|
+
neutral: "#4A3F70",
|
|
14
|
+
seq: ["#1A1A24", "#2D2A42", "#4A3F70", "#7C5FE6", "#9B7CFF", "#D6CCFF"]
|
|
15
|
+
},
|
|
16
|
+
cvdSafe: {
|
|
17
|
+
cat: ["#0072B2", "#E69F00", "#009E73", "#CC79A7", "#56B4E9", "#F0E442"],
|
|
18
|
+
primary: "#0072B2",
|
|
19
|
+
accent: "#E69F00",
|
|
20
|
+
neutral: "#A3C9E2",
|
|
21
|
+
seq: ["#F0F7FB", "#D5E5F0", "#A3C9E2", "#56B4E9", "#0072B2", "#003E5C"]
|
|
22
|
+
},
|
|
23
|
+
cvdSafeDark: {
|
|
24
|
+
cat: ["#4DA8E5", "#FFB84D", "#3DD9A8", "#E5A0C8", "#7DD3F5", "#F5E76A"],
|
|
25
|
+
primary: "#4DA8E5",
|
|
26
|
+
accent: "#FFB84D",
|
|
27
|
+
neutral: "#3A4A5C",
|
|
28
|
+
seq: ["#1A1A24", "#2A3A4A", "#3A5A78", "#4DA8E5", "#7DD3F5", "#B3E5FC"]
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
export const RM_PRODUCT_PALETTES = {
|
|
32
|
+
generic: ["#2100AD", "#4164EE", "#7B9EF5", "#B8C8F8", "#DBDEF0"],
|
|
33
|
+
healthcare: ["#4164EE", "#1A2A8F", "#00D4A8", "#7B9EF5", "#B8C8F8"],
|
|
34
|
+
pharma: ["#F7520E", "#FF8C00", "#FFD600", "#FCA982", "#DBDEF0"],
|
|
35
|
+
discovery: ["#07816F", "#2ECC71", "#56CCF2", "#0DA38D", "#DBDEF0"]
|
|
36
|
+
};
|
|
37
|
+
export const RM_DIVERGING = ["#E53535", "#FCA5A5", "#F9F9FA", "#86EFAC", "#16A34A"];
|
|
38
|
+
const THEME_DAY = {
|
|
39
|
+
night: false,
|
|
40
|
+
textColor: "#1E293B",
|
|
41
|
+
mutedColor: "#525670",
|
|
42
|
+
dimmedColor: "#878CA6",
|
|
43
|
+
splitLine: "#F0F1F7",
|
|
44
|
+
axisLine: "#DBDEF0",
|
|
45
|
+
tooltipBg: "#1E293B",
|
|
46
|
+
tooltipText: "#FFFFFF",
|
|
47
|
+
tooltipBorder: "#344159",
|
|
48
|
+
backgroundColor: "transparent"
|
|
49
|
+
};
|
|
50
|
+
const THEME_NIGHT = {
|
|
51
|
+
night: true,
|
|
52
|
+
textColor: "#E5E7F0",
|
|
53
|
+
mutedColor: "#B8BCD0",
|
|
54
|
+
dimmedColor: "#7B8099",
|
|
55
|
+
splitLine: "#2F2F40",
|
|
56
|
+
axisLine: "#3A3A4E",
|
|
57
|
+
tooltipBg: "#232330",
|
|
58
|
+
tooltipText: "#E5E7F0",
|
|
59
|
+
tooltipBorder: "#3A3A4E",
|
|
60
|
+
backgroundColor: "transparent"
|
|
61
|
+
};
|
|
62
|
+
export const rmChartTheme = (night) => night ? THEME_NIGHT : THEME_DAY;
|
|
63
|
+
export function rmPalette(name = "default", night = false) {
|
|
64
|
+
const dark = `${name}Dark`;
|
|
65
|
+
if (night && RM_PALETTES[dark]) return RM_PALETTES[dark];
|
|
66
|
+
return RM_PALETTES[name] ?? RM_PALETTES.default;
|
|
67
|
+
}
|
|
68
|
+
export const RM_CHART_HEIGHTS = {
|
|
69
|
+
sparkline: 48,
|
|
70
|
+
kpi: 80,
|
|
71
|
+
compact: 200,
|
|
72
|
+
standard: 260,
|
|
73
|
+
large: 320,
|
|
74
|
+
full: 360
|
|
75
|
+
};
|
|
76
|
+
const FONT = "Figtree, system-ui, sans-serif";
|
|
77
|
+
export function rmBaseOption(theme, decal = false) {
|
|
78
|
+
return {
|
|
79
|
+
backgroundColor: theme.backgroundColor,
|
|
80
|
+
animation: true,
|
|
81
|
+
aria: { enabled: true, decal: { show: decal } },
|
|
82
|
+
textStyle: { fontFamily: FONT },
|
|
83
|
+
grid: { left: 48, right: 16, top: 24, bottom: 36, containLabel: true },
|
|
84
|
+
tooltip: {
|
|
85
|
+
trigger: "axis",
|
|
86
|
+
backgroundColor: theme.tooltipBg,
|
|
87
|
+
textStyle: { color: theme.tooltipText, fontFamily: FONT, fontSize: 12 },
|
|
88
|
+
borderColor: theme.tooltipBorder,
|
|
89
|
+
borderWidth: 1
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
const axisCommon = (theme) => ({
|
|
94
|
+
axisLabel: { fontSize: 11, color: theme.mutedColor, fontFamily: FONT },
|
|
95
|
+
axisLine: { lineStyle: { color: theme.axisLine } },
|
|
96
|
+
splitLine: { lineStyle: { color: theme.splitLine } }
|
|
97
|
+
});
|
|
98
|
+
export function rmBarVerticalOption(categories, series, theme, palette, decal = false) {
|
|
99
|
+
return {
|
|
100
|
+
...rmBaseOption(theme, decal),
|
|
101
|
+
legend: series.length > 1 ? { top: 0, textStyle: { color: theme.mutedColor, fontSize: 11, fontFamily: FONT } } : void 0,
|
|
102
|
+
xAxis: { type: "category", data: categories, ...axisCommon(theme), splitLine: { show: false } },
|
|
103
|
+
yAxis: { type: "value", ...axisCommon(theme) },
|
|
104
|
+
series: series.map((s, i) => ({
|
|
105
|
+
type: "bar",
|
|
106
|
+
name: s.name,
|
|
107
|
+
stack: s.stack,
|
|
108
|
+
data: s.data,
|
|
109
|
+
itemStyle: { color: s.color ?? palette[i % palette.length], borderRadius: [3, 3, 0, 0] }
|
|
110
|
+
}))
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
export function rmBarHorizontalOption(labels, values, theme, palette, decal = false, formatter) {
|
|
114
|
+
return {
|
|
115
|
+
...rmBaseOption(theme, decal),
|
|
116
|
+
grid: { left: 140, right: 60, top: 16, bottom: 24, containLabel: true },
|
|
117
|
+
xAxis: { type: "value", ...axisCommon(theme) },
|
|
118
|
+
yAxis: { type: "category", data: labels, ...axisCommon(theme), splitLine: { show: false } },
|
|
119
|
+
series: [{
|
|
120
|
+
type: "bar",
|
|
121
|
+
data: values,
|
|
122
|
+
itemStyle: {
|
|
123
|
+
color: (params) => palette[params.dataIndex % palette.length],
|
|
124
|
+
borderRadius: [0, 3, 3, 0]
|
|
125
|
+
},
|
|
126
|
+
label: {
|
|
127
|
+
show: true,
|
|
128
|
+
position: "right",
|
|
129
|
+
fontSize: 11,
|
|
130
|
+
color: theme.mutedColor,
|
|
131
|
+
fontFamily: FONT,
|
|
132
|
+
formatter: formatter ? (p) => formatter(p.value) : void 0
|
|
133
|
+
}
|
|
134
|
+
}]
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
export function rmLineOption(categories, series, theme, palette, options = {}) {
|
|
138
|
+
const { area = false, step = false, smooth = true, decal = false } = options;
|
|
139
|
+
return {
|
|
140
|
+
...rmBaseOption(theme, decal),
|
|
141
|
+
legend: series.length > 1 ? { top: 0, textStyle: { color: theme.mutedColor, fontSize: 11, fontFamily: FONT } } : void 0,
|
|
142
|
+
xAxis: { type: "category", data: categories, ...axisCommon(theme), splitLine: { show: false }, boundaryGap: false },
|
|
143
|
+
yAxis: { type: "value", ...axisCommon(theme) },
|
|
144
|
+
series: series.map((s, i) => {
|
|
145
|
+
const color = s.color ?? palette[i % palette.length];
|
|
146
|
+
return {
|
|
147
|
+
type: "line",
|
|
148
|
+
name: s.name,
|
|
149
|
+
data: s.data,
|
|
150
|
+
smooth: step ? false : smooth,
|
|
151
|
+
step: step ? "end" : void 0,
|
|
152
|
+
symbol: "circle",
|
|
153
|
+
symbolSize: 5,
|
|
154
|
+
lineStyle: { color, width: 2.5 },
|
|
155
|
+
itemStyle: { color },
|
|
156
|
+
areaStyle: area ? {
|
|
157
|
+
color: {
|
|
158
|
+
type: "linear",
|
|
159
|
+
x: 0,
|
|
160
|
+
y: 0,
|
|
161
|
+
x2: 0,
|
|
162
|
+
y2: 1,
|
|
163
|
+
colorStops: [
|
|
164
|
+
{ offset: 0, color: `${color}30` },
|
|
165
|
+
{ offset: 1, color: `${color}00` }
|
|
166
|
+
]
|
|
167
|
+
}
|
|
168
|
+
} : void 0
|
|
169
|
+
};
|
|
170
|
+
})
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
export function rmDonutOption(data, theme, palette, decal = false) {
|
|
174
|
+
return {
|
|
175
|
+
...rmBaseOption(theme, decal),
|
|
176
|
+
grid: void 0,
|
|
177
|
+
tooltip: { ...rmBaseOption(theme, decal).tooltip, trigger: "item" },
|
|
178
|
+
legend: { bottom: 0, textStyle: { color: theme.mutedColor, fontSize: 11, fontFamily: FONT } },
|
|
179
|
+
series: [{
|
|
180
|
+
type: "pie",
|
|
181
|
+
radius: ["45%", "70%"],
|
|
182
|
+
center: ["50%", "46%"],
|
|
183
|
+
data: data.map((d, i) => ({
|
|
184
|
+
value: d.value,
|
|
185
|
+
name: d.name,
|
|
186
|
+
itemStyle: { color: palette[i % palette.length] }
|
|
187
|
+
})),
|
|
188
|
+
label: { fontSize: 11, color: theme.mutedColor, fontFamily: FONT }
|
|
189
|
+
}]
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
export function rmSparklineOption(values, color) {
|
|
193
|
+
return {
|
|
194
|
+
backgroundColor: "transparent",
|
|
195
|
+
animation: true,
|
|
196
|
+
grid: { left: 0, right: 0, top: 2, bottom: 2 },
|
|
197
|
+
xAxis: { type: "category", show: false, data: values.map((_, i) => i) },
|
|
198
|
+
yAxis: { type: "value", show: false },
|
|
199
|
+
tooltip: { show: false },
|
|
200
|
+
series: [{
|
|
201
|
+
type: "line",
|
|
202
|
+
data: values,
|
|
203
|
+
smooth: true,
|
|
204
|
+
symbol: "none",
|
|
205
|
+
lineStyle: { color, width: 1.5 },
|
|
206
|
+
areaStyle: { color: `${color}20` }
|
|
207
|
+
}]
|
|
208
|
+
};
|
|
209
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tekkare/romulus",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
4
4
|
"description": "Romulus Design System - Nuxt module by Tekkare",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -27,7 +27,8 @@
|
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"@nuxt/kit": "^3.16.0",
|
|
30
|
-
"@phosphor-icons/vue": "^2.2.1"
|
|
30
|
+
"@phosphor-icons/vue": "^2.2.1",
|
|
31
|
+
"echarts": "^5.6.0"
|
|
31
32
|
},
|
|
32
33
|
"devDependencies": {
|
|
33
34
|
"@nuxt/module-builder": "^0.8.4",
|