nuxt-ui-basekit 0.1.1 → 0.2.1

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.
@@ -1,18 +1,18 @@
1
1
  <script setup lang="ts">
2
2
  /**
3
- * Gestapelte Säulen über einer Zeitachse„was ist je Tag entstanden".
3
+ * Stacked columns over a time axis what accrued per day.
4
4
  *
5
- * Warum gestapelt und nicht mehrere Linien: die Frage ist Anteil am Ganzen,
6
- * nicht Verlauf einzelner Reihen. Und warum Säulen und keine Fläche: Tageswerte
7
- * sind gezählte Ereignisse, keine stetige Größe eine Fläche würde zwischen
8
- * zwei Tagen interpolieren und behaupten, dass um 12 Uhr nachts etwas
9
- * halb passiert ist.
5
+ * Stacked rather than several lines because the question is share of the
6
+ * whole, not the course of individual series. Columns rather than an area
7
+ * because daily values are counted events, not a continuous quantity: an area
8
+ * would interpolate between two days and claim something half-happened at
9
+ * midnight.
10
10
  *
11
- * Trennung zwischen den Segmenten passiert über eine 2-px-Lücke in der
12
- * Kartenfarbe, nicht über einen Rahmen: ein Rahmen ist Tinte, die keine Daten
13
- * trägt, und bei dünnen Segmenten überdeckt er den Wert.
11
+ * Segments are separated by a 2px gap in the card colour rather than a border.
12
+ * A border is ink that carries no data, and on thin segments it covers the
13
+ * value.
14
14
  *
15
- * Höchstens fünf Reihensiehe `useChartPalette`.
15
+ * Five series at most see `useChartPalette`.
16
16
  */
17
17
  import { computed, ref } from 'vue'
18
18
  import { baseKitChartColor, useChartFormat } from '../../composables/useChartPalette'
@@ -25,13 +25,13 @@ interface ColumnSeries {
25
25
  }
26
26
 
27
27
  const props = withDefaults(defineProps<{
28
- /** Beschriftung je Säule, z. B. ISO-Tage. */
28
+ /** Label per column, ISO dates for instance. */
29
29
  categories: string[]
30
30
  series: ColumnSeries[]
31
31
  height?: number
32
- /** Achsenbeschriftung (kurz) aus einer Kategorie. */
32
+ /** Short axis label derived from a category. */
33
33
  formatCategory?: (value: string) => string
34
- /** Ausführliche Fassung für den Tooltip. */
34
+ /** The long form, for the tooltip. */
35
35
  formatCategoryLong?: (value: string) => string
36
36
  ariaLabel?: string
37
37
  }>(), {
@@ -64,7 +64,7 @@ const plot = computed(() => {
64
64
  }
65
65
  })
66
66
 
67
- /** Tagessummensie bestimmen die Skala und stehen im Tooltip. */
67
+ /** Daily totals they set the scale and appear in the tooltip. */
68
68
  const totals = computed(() =>
69
69
  props.categories.map((_, index) =>
70
70
  props.series.reduce((sum, s) => sum + (s.points[index] ?? 0), 0),
@@ -72,9 +72,8 @@ const totals = computed(() =>
72
72
  )
73
73
 
74
74
  /**
75
- * Obere Achsengrenze auf eine runde Zahl aufgerundet. Ohne das steht die
76
- * Beschriftung auf krummen Werten wie 37, und die Gitterlinien tragen keine
77
- * ablesbare Auskunft mehr.
75
+ * Upper axis bound, rounded up to something even. Without it the labels sit on
76
+ * awkward values like 37 and the grid lines stop telling anyone anything.
78
77
  */
79
78
  const scaleMax = computed(() => {
80
79
  const peak = Math.max(0, ...totals.value)
@@ -100,7 +99,7 @@ function columnX(index: number): number {
100
99
  return PADDING.left + plot.value.band * (index + 0.5) - plot.value.columnWidth / 2
101
100
  }
102
101
 
103
- /** Runde Kappe nur obenunten sitzt die Säule auf der Grundlinie auf. */
102
+ /** Rounded cap on top only the column sits flat on the baseline. */
104
103
  function cappedPath(x: number, y: number, w: number, h: number): string {
105
104
  const r = Math.min(4, w / 2, h)
106
105
  if (h <= 0) return ''
@@ -129,7 +128,7 @@ const columns = computed(() => {
129
128
  const segments: Segment[] = []
130
129
  let stacked = 0
131
130
 
132
- // Von unten nach oben stapeln; die oberste Reihe bekommt die Kappe.
131
+ // Stack bottom to top; the topmost series gets the cap.
133
132
  const present = props.series
134
133
  .map((s, order) => ({ s, order, value: s.points[index] ?? 0 }))
135
134
  .filter(entry => entry.value > 0)
@@ -140,8 +139,8 @@ const columns = computed(() => {
140
139
  const top = baseline - (stacked / max) * innerHeight
141
140
 
142
141
  const isTop = position === present.length - 1
143
- // Lücke nach oben, außer beim obersten Segmentdort säße sie
144
- // zwischen Säule und Luft und würde die Säule nur kürzen.
142
+ // A gap above, except on the topmost segment there it would sit
143
+ // between the column and thin air and merely shorten it.
145
144
  const height = Math.max(0, bottom - top - (isTop ? 0 : GAP))
146
145
  if (height <= 0) return
147
146
 
@@ -159,9 +158,9 @@ const columns = computed(() => {
159
158
  })
160
159
 
161
160
  /**
162
- * Beschriftung ausdünnen, bis sie nicht mehr kollidiertund zwar durch
163
- * Weglassen, nicht durch Verstecken: ein ausgeblendeter Text bleibt im
164
- * Baum stehen und wird von Screenreadern trotzdem vorgelesen.
161
+ * Thin the labels out until they stop colliding by leaving them out, not by
162
+ * hiding them: hidden text stays in the tree and screen readers read it
163
+ * anyway.
165
164
  */
166
165
  const axisLabels = computed(() => {
167
166
  const perLabel = 56
@@ -194,7 +193,7 @@ const tooltip = computed(() => {
194
193
  value: s.points[index] ?? 0,
195
194
  }))
196
195
  .filter(row => row.value > 0),
197
- // Am Rand kippt der Kasten nach innen, statt aus der Karte zu laufen.
196
+ // Near the edge the box flips inward instead of running off the card.
198
197
  anchor: PADDING.left + plot.value.band * (index + 0.5),
199
198
  }
200
199
  })
@@ -218,7 +217,7 @@ function readout(index: number): string {
218
217
  :aria-label="props.ariaLabel"
219
218
  class="block max-w-full overflow-visible"
220
219
  >
221
- <!-- Gitter: durchgezogene Haarlinien, eine Stufe von der Fläche weg. -->
220
+ <!-- Grid: solid hairlines, one step away from the surface. -->
222
221
  <g>
223
222
  <line
224
223
  v-for="tick in ticks"
@@ -242,7 +241,7 @@ function readout(index: number): string {
242
241
  >{{ tick.label }}</text>
243
242
  </g>
244
243
 
245
- <!-- Datenmarken -->
244
+ <!-- Data marks -->
246
245
  <g
247
246
  v-for="column in columns"
248
247
  :key="column.category"
@@ -257,8 +256,8 @@ function readout(index: number): string {
257
256
  />
258
257
  </g>
259
258
 
260
- <!-- Trefferflächen: über die volle Höhe und über die ganze Bandbreite,
261
- damit niemand eine 14 px schmale Säule treffen muss. -->
259
+ <!-- Hit areas: full height and the whole band, so nobody has to hit a
260
+ 14px column. -->
262
261
  <g>
263
262
  <rect
264
263
  v-for="column in columns"
@@ -279,7 +278,7 @@ function readout(index: number): string {
279
278
  />
280
279
  </g>
281
280
 
282
- <!-- Achsenbeschriftung -->
281
+ <!-- Axis labels -->
283
282
  <text
284
283
  v-for="label in axisLabels"
285
284
  :key="`label-${label.category}`"
@@ -291,7 +290,7 @@ function readout(index: number): string {
291
290
  >{{ label.text }}</text>
292
291
  </svg>
293
292
 
294
- <!-- Tooltip: Wert führt, Reihenname folgt. -->
293
+ <!-- Tooltip: the value leads, the series name follows. -->
295
294
  <div
296
295
  v-if="tooltip"
297
296
  class="pointer-events-none absolute top-0 z-10 min-w-40 -translate-x-1/2 rounded-md border border-default bg-default p-2 shadow-lg"
@@ -1,14 +1,14 @@
1
1
  <script setup lang="ts">
2
2
  /**
3
- * Ring für Teil-vom-Ganzen bei wenigen Klassen.
3
+ * A ring for part-of-whole across a few classes.
4
4
  *
5
- * Bewusst eng eingesetzt: ein Ring beantwortet „wie verteilt sich das grob",
6
- * nicht „welcher von zweien ist größer". Sobald zwei Segmente nahe beieinander
7
- * liegen, ist ein Balken die ehrlichere Form. Höchstens vier Segmente, sonst
8
- * verlieren die kleinen ihre Beschriftung.
5
+ * Deliberately narrow in scope: a ring answers "roughly how does this split",
6
+ * not "which of these two is bigger". As soon as two segments sit close
7
+ * together, a bar is the more honest shape. Four segments at most, otherwise
8
+ * the small ones lose their labels.
9
9
  *
10
- * In der Mitte steht die Summedas ist die Zahl, die zuerst gesucht wird,
11
- * und sie füllt den Platz, den ein Ring ohnehin frei lässt.
10
+ * The total goes in the middleit is the number people look for first, and
11
+ * it fills the space a ring leaves empty anyway.
12
12
  */
13
13
  import { computed, ref } from 'vue'
14
14
  import { baseKitChartColor, useChartFormat } from '../../composables/useChartPalette'
@@ -21,7 +21,7 @@ interface DonutSlice {
21
21
 
22
22
  const props = withDefaults(defineProps<{
23
23
  slices: DonutSlice[]
24
- /** Beschriftung unter der Summe in der Mitte. */
24
+ /** Label below the total in the centre. */
25
25
  centerLabel?: string
26
26
  size?: number
27
27
  ariaLabel?: string
@@ -34,7 +34,7 @@ const props = withDefaults(defineProps<{
34
34
  const { number, percent } = useChartFormat()
35
35
 
36
36
  const THICKNESS = 18
37
- /** Lücke zwischen den Segmenten, in Graddas Gegenstück zur 2-px-Lücke. */
37
+ /** Gap between segments, in degreesthe counterpart to the 2px gap. */
38
38
  const GAP_DEGREES = 2
39
39
 
40
40
  const active = ref<string | null>(null)
@@ -61,8 +61,8 @@ const arcs = computed(() => {
61
61
  const start = cursor
62
62
  cursor += sweep
63
63
 
64
- // Ein Segment, das den ganzen Kreis füllt, hätte identischen Start- und
65
- // Endpunkt der Bogen verschwände. Dann zeichnen wir den vollen Ring.
64
+ // A segment filling the whole circle would have identical start and end
65
+ // points and the arc would vanish. Draw the full ring instead.
66
66
  const full = sweep >= 359.9
67
67
  const gap = full ? 0 : Math.min(GAP_DEGREES, sweep / 3)
68
68
  const from = start + gap / 2
@@ -95,10 +95,10 @@ const legend = computed(() =>
95
95
  </script>
96
96
 
97
97
  <template>
98
- <!-- Legende unter dem Ring, nicht daneben: die Karte steht auf der
99
- Übersicht in einer schmalen Spalte, und nebeneinander blieb für die
100
- Beschriftungen so wenig übrig, dass „Ausgeschieden" auf ein „A"
101
- zusammenschrumpfte. Untereinander hat jede Zeile die volle Breite. -->
98
+ <!-- Legend below the ring, not beside it: the card often sits in a narrow
99
+ column, and side by side so little was left for the labels that a word
100
+ like "Discontinued" shrank to a "D". Stacked, every row has the full
101
+ width. -->
102
102
  <div class="flex flex-col items-center gap-5">
103
103
  <svg
104
104
  :width="props.size"
@@ -108,8 +108,8 @@ const legend = computed(() =>
108
108
  :aria-label="props.ariaLabel"
109
109
  class="shrink-0"
110
110
  >
111
- <!-- Leerer Ring, wenn nichts da ist: die Form bleibt erkennbar, statt
112
- dass die Karte auf eine leere Fläche zusammenfällt. -->
111
+ <!-- An empty ring when there is nothing: the shape stays recognisable
112
+ instead of the card collapsing into a blank area. -->
113
113
  <circle
114
114
  v-if="!arcs.length"
115
115
  :cx="center"
@@ -156,7 +156,7 @@ const legend = computed(() =>
156
156
  >{{ props.centerLabel }}</text>
157
157
  </svg>
158
158
 
159
- <!-- Legende trägt die Identität, nicht die Farbe allein. -->
159
+ <!-- The legend carries the identity, not the colour alone. -->
160
160
  <ul class="w-full space-y-1.5">
161
161
  <li
162
162
  v-for="entry in legend"
@@ -1,13 +1,13 @@
1
1
  <script setup lang="ts">
2
2
  /**
3
- * Rahmen für ein Diagramm: Karte, Titel, Legende und der Umschalter auf die
4
- * Tabellenansicht.
3
+ * The frame around a chart: card, title, legend, and the switch to the table
4
+ * view.
5
5
  *
6
- * Der Umschalter ist keine Zugabe. Ein Diagramm codiert Werte über Farbe und
7
- * Länge; wer davon nichts hatFarbfehlsichtigkeit, Screenreader, Ausdruck —
8
- * braucht dieselben Zahlen in lesbarer Form. Deshalb bringt jede Karte hier
9
- * ihre Tabelle mit, und deshalb ist der Umschalter sichtbar und nicht in einem
10
- * Menü versteckt.
6
+ * That switch is not a nicety. A chart encodes values through colour and
7
+ * length; anyone who gets nothing out of that a colour vision deficiency, a
8
+ * screen reader, a printout needs the same numbers in readable form. Which
9
+ * is why every card here brings its table along, and why the switch is visible
10
+ * rather than buried in a menu.
11
11
  */
12
12
  import { computed, ref } from 'vue'
13
13
  import { useBaseKitLabels } from '../../composables/useBaseKit'
@@ -15,11 +15,11 @@ import { useBaseKitLabels } from '../../composables/useBaseKit'
15
15
  const props = withDefaults(defineProps<{
16
16
  title: string
17
17
  subtitle?: string
18
- /** Wohin die Karte führt. Ohne Ziel bleibt der Titel ein Titel. */
18
+ /** Where the card leads. Without a target the title stays a title. */
19
19
  href?: string
20
- /** Diagramm ausgrauen, während im Hintergrund nachgeladen wird. */
20
+ /** Grey the chart out while something reloads in the background. */
21
21
  stale?: boolean
22
- /** Ohne Tabellen-Slot entfällt der Umschalter. */
22
+ /** Without a table slot the switch disappears. */
23
23
  tableAvailable?: boolean
24
24
  }>(), {
25
25
  subtitle: undefined,
@@ -1,10 +1,10 @@
1
1
  <script setup lang="ts">
2
2
  /**
3
- * Ein Verhältnis gegen eine bekannte Grenze„so viele von so vielen".
3
+ * A ratio against a known limitthis many of that many.
4
4
  *
5
- * Ein Meter setzt voraus, dass es ein Ganzes gibt. Wo keins existiert (Bytes
6
- * ohne Kontingent, Anzahl ohne Obergrenze), gehört die Zahl in eine Kachel und
7
- * nicht hierher; ein voller Balken behauptete sonst eine erreichte Grenze.
5
+ * A meter presupposes that a whole exists. Where none does bytes without a
6
+ * quota, a count without a ceiling the number belongs in a tile and not
7
+ * here; a full bar would otherwise claim a limit had been reached.
8
8
  */
9
9
  import { computed } from 'vue'
10
10
  import { baseKitChartColor, useChartFormat } from '../../composables/useChartPalette'
@@ -1,55 +1,54 @@
1
1
  import { computed, inject, type ComputedRef, type InjectionKey } from 'vue'
2
2
 
3
3
  /**
4
- * Der einzige Draht zwischen den BaseKit-Komponenten und der Anwendung,
5
- * die sie einsetzt.
4
+ * The only wire between the BaseKit components and the application using them.
6
5
  *
7
- * Die Komponenten sollen in jedem Nuxt-Projekt laufen. Sie dürfen deshalb
8
- * weder `vue-i18n` aufrufen noch Übersetzungsschlüssel kennen: `t('common.search')`
9
- * gibt es nur in der einen Anwendung, die diesen Schlüssel führt, und ein
10
- * Paket, das solche Schlüssel voraussetzt, ist keins.
6
+ * The components are meant to run in any Nuxt project. That rules out calling
7
+ * `vue-i18n` and it rules out knowing translation keys: `t('common.search')`
8
+ * exists only in the one application that carries that key, and a package
9
+ * which presupposes such keys is not a package.
11
10
  *
12
- * Stattdessen reicht die Anwendung Sprache und Beschriftungen einmal herein
13
- * in einem Plugin, das die Werte aus ihrer eigenen Quelle zieht:
11
+ * Instead the application hands language and labels in once, from a plugin
12
+ * that pulls the values out of its own source:
14
13
  *
15
14
  * // plugins/basekit.ts
16
15
  * export default defineNuxtPlugin((nuxtApp) => {
17
16
  * const config = computed(() => ({
18
17
  * locale: 'de-DE',
19
- * labels: { ...BASEKIT_DEFAULTS.labels, search: 'Suchen' },
18
+ * labels: { ...BASEKIT_DEFAULTS.labels, search: 'Suchen', cancel: 'Abbrechen' },
20
19
  * }))
21
20
  * nuxtApp.vueApp.provide(baseKitKey, config)
22
21
  * })
23
22
  *
24
- * Wer nichts bereitstellt, bekommt die Voreinstellungen unten. Wichtig, wenn
25
- * die Werte aus einer i18n-Bibliothek kommen: `useI18n()` verlangt einen
26
- * Komponenten-Setup-Kontext und wirft im Plugin. Die Instanz gehört über
27
- * `nuxtApp.$i18n` geholt, und zwar erst beim Lesen des `computed`.
23
+ * Provide nothing and the defaults below apply. One thing worth knowing when
24
+ * the values come from an i18n library: `useI18n()` requires a component setup
25
+ * context and throws inside a plugin. Take the instance from `nuxtApp.$i18n`,
26
+ * and only when the `computed` is read.
28
27
  *
29
- * Über `provide`/`inject`, nicht über `useNuxtApp()`: so laufen die
30
- * Komponenten auch in einem Vitest-Mount ohne Nuxt-Kontext, und unter SSR
31
- * teilen sich zwei Anfragen nichts.
28
+ * Over `provide`/`inject` rather than `useNuxtApp()`: that way the components
29
+ * also run in a Vitest mount without a Nuxt context, and two requests share
30
+ * nothing under SSR.
32
31
  *
33
- * Einzelne Beschriftungen bleiben weiterhin als Prop überschreibbar hier
34
- * steht nur, was ohne Zutun herauskommt.
32
+ * Individual labels stay overridable as propswhat follows only decides what
33
+ * comes out when nothing is passed.
35
34
  */
36
35
  export interface BaseKitLabels {
37
- /** Suchfeld über Listen und Auswahl-Dialogen. */
36
+ /** Search field above lists and pickers. */
38
37
  search: string
39
- /** Filter-Eintrag „ohne Einschränkung". */
38
+ /** Filter entry for "no restriction". */
40
39
  all: string
41
40
  select: string
42
41
  change: string
43
42
  edit: string
44
43
  cancel: string
45
44
  confirm: string
46
- /** Überschrift der Rückfrage, wenn der Aufrufer keine mitgibt. */
45
+ /** Heading of the confirmation when the caller passes none. */
47
46
  confirmTitle: string
48
- /** Text der Rückfrage, wenn der Aufrufer keinen mitgibt. */
47
+ /** Body of the confirmation when the caller passes none. */
49
48
  confirmBody: string
50
- /** Leere Listees gibt noch nichts. */
49
+ /** Empty listnothing exists yet. */
51
50
  empty: string
52
- /** Leere Listedie Suche greift, findet aber nichts. */
51
+ /** Empty listthe search ran and found nothing. */
53
52
  noResults: string
54
53
  noResultsHint: string
55
54
  back: string
@@ -58,15 +57,15 @@ export interface BaseKitLabels {
58
57
  perPage: string
59
58
  iconChoose: string
60
59
  iconEmpty: string
61
- /** Auswahl aufhebenim Symbolwähler die leere Wahl. */
60
+ /** Clearing the choice the empty option in the icon picker. */
62
61
  iconClear: string
63
62
  chartAsTable: string
64
63
  chartAsChart: string
65
- /** 1–25 von 300" — als Funktion, weil die Zahlen mitten im Satz stehen. */
64
+ /** "1–25 of 300" — a function, because the numbers sit inside the sentence. */
66
65
  paginationRange: (range: { from: number, to: number, total: number }) => string
67
66
  /**
68
- * Werkzeugleiste des Markdown-Editors. Eigene Gruppe, weil die Begriffe nur
69
- * dort vorkommen und die obere Ebene sonst zur Hälfte aus ihnen bestünde.
67
+ * Toolbar of the Markdown editor. Its own group, because the terms occur
68
+ * nowhere else and the top level would otherwise be half made of them.
70
69
  */
71
70
  markdown: {
72
71
  bold: string
@@ -78,60 +77,61 @@ export interface BaseKitLabels {
78
77
  quote: string
79
78
  code: string
80
79
  link: string
81
- /** Abfrage beim Setzen eines Links. */
80
+ /** Prompt shown when setting a link. */
82
81
  linkPrompt: string
83
82
  }
84
83
  }
85
84
 
86
85
  export interface BaseKitConfig {
87
86
  /**
88
- * BCP-47-Kennung für `Intl` — steuert Tausendertrennung, Prozent- und
89
- * Datumsformate in den Diagrammen.
87
+ * BCP-47 tag for `Intl` — drives thousands separators, percentages and date
88
+ * formats in the charts.
90
89
  */
91
90
  locale: string
92
91
  labels: BaseKitLabels
93
92
  }
94
93
 
95
94
  /**
96
- * Voreinstellung: deutsch. Alle Projekte im Haus sind es, und eine Anwendung
97
- * mit mehreren Sprachen überschreibt die Tabelle ohnehin komplett.
95
+ * Defaults: English. The package cannot know what language the application
96
+ * runs in, and English is the one that shuts out the fewest readers. Anything
97
+ * else is handed in through the plugin above.
98
98
  */
99
99
  export const BASEKIT_DEFAULTS: BaseKitConfig = {
100
- locale: 'de-DE',
100
+ locale: 'en-US',
101
101
  labels: {
102
- search: 'Suchen',
103
- all: 'Alle',
104
- select: 'Auswählen',
105
- change: 'Ändern',
106
- edit: 'Bearbeiten',
107
- cancel: 'Abbrechen',
108
- confirm: 'Bestätigen',
109
- confirmTitle: 'Sind Sie sicher?',
110
- confirmBody: 'Diese Aktion lässt sich nicht rückgängig machen.',
111
- empty: 'Noch nichts vorhanden',
112
- noResults: 'Keine Treffer',
113
- noResultsHint: 'Andere Schreibweise oder weniger Filter probieren.',
114
- back: 'Zurück zur Übersicht',
115
- view: 'Ansehen',
116
- upload: 'Datei wählen',
117
- perPage: 'pro Seite',
118
- iconChoose: 'Symbol wählen',
119
- iconEmpty: 'Kein Symbol gefunden',
120
- iconClear: 'Kein Symbol',
121
- chartAsTable: 'Als Tabelle',
122
- chartAsChart: 'Als Diagramm',
123
- paginationRange: ({ from, to, total }) => `${from}–${to} von ${total}`,
102
+ search: 'Search',
103
+ all: 'All',
104
+ select: 'Select',
105
+ change: 'Change',
106
+ edit: 'Edit',
107
+ cancel: 'Cancel',
108
+ confirm: 'Confirm',
109
+ confirmTitle: 'Are you sure?',
110
+ confirmBody: 'This action cannot be undone.',
111
+ empty: 'Nothing here yet',
112
+ noResults: 'No matches',
113
+ noResultsHint: 'Try a different spelling or fewer filters.',
114
+ back: 'Back to overview',
115
+ view: 'View',
116
+ upload: 'Choose file',
117
+ perPage: 'per page',
118
+ iconChoose: 'Choose icon',
119
+ iconEmpty: 'No icon found',
120
+ iconClear: 'No icon',
121
+ chartAsTable: 'As table',
122
+ chartAsChart: 'As chart',
123
+ paginationRange: ({ from, to, total }) => `${from}–${to} of ${total}`,
124
124
  markdown: {
125
- bold: 'Fett',
126
- italic: 'Kursiv',
127
- h2: 'Überschrift 2',
128
- h3: 'Überschrift 3',
129
- bullet: 'Liste',
130
- ordered: 'Nummerierte Liste',
131
- quote: 'Zitat',
125
+ bold: 'Bold',
126
+ italic: 'Italic',
127
+ h2: 'Heading 2',
128
+ h3: 'Heading 3',
129
+ bullet: 'Bullet list',
130
+ ordered: 'Numbered list',
131
+ quote: 'Quote',
132
132
  code: 'Code',
133
133
  link: 'Link',
134
- linkPrompt: 'Link-URL',
134
+ linkPrompt: 'Link URL',
135
135
  },
136
136
  },
137
137
  }
@@ -139,16 +139,16 @@ export const BASEKIT_DEFAULTS: BaseKitConfig = {
139
139
  export const baseKitKey: InjectionKey<ComputedRef<BaseKitConfig>> = Symbol('basekit')
140
140
 
141
141
  /**
142
- * Konfiguration für eine BaseKit-Komponente. Ohne bereitgestellten Wert
143
- * greifen die Voreinstellungen die Komponente rendert dann auf Deutsch,
144
- * statt leere Beschriftungen zu zeigen.
142
+ * Configuration for a BaseKit component. With nothing provided the defaults
143
+ * apply the component then renders in English instead of showing empty
144
+ * labels.
145
145
  */
146
146
  export function useBaseKit(): ComputedRef<BaseKitConfig> {
147
147
  const provided = inject(baseKitKey, null)
148
148
  return provided ?? computed(() => BASEKIT_DEFAULTS)
149
149
  }
150
150
 
151
- /** Kurzform für den häufigen Fall, dass nur die Beschriftungen gebraucht werden. */
151
+ /** Short form for the common case of only needing the labels. */
152
152
  export function useBaseKitLabels(): ComputedRef<BaseKitLabels> {
153
153
  const config = useBaseKit()
154
154
  return computed(() => config.value.labels)
@@ -2,17 +2,17 @@ import { computed } from 'vue'
2
2
  import { useBaseKit } from './useBaseKit'
3
3
 
4
4
  /**
5
- * Reihenfarben und Zahlenformate für die `BaseKitChart*`-Komponenten.
5
+ * Series colours and number formats for the `BaseKitChart*` components.
6
6
  *
7
- * Die Farben stehen als CSS-Variablen in `main.css` und sind als **Satz**
8
- * geprüft Reihenfolge inklusive. Deshalb gibt es hier nur einen Zugriff per
9
- * Index und keine Erzeugung: eine sechste Farbe wäre unter einer
10
- * Farbfehlsichtigkeit von einer der fünf nicht mehr zu unterscheiden.
11
- * Wer mehr Reihen hat, fasst zusammen.
7
+ * The colours live as CSS variables in `app/assets/css/basekit.css` and are
8
+ * validated as a **set**, order included. That is why there is lookup by index
9
+ * here and no generation: a sixth colour would be indistinguishable from one
10
+ * of the five under a colour vision deficiency. More series than that means
11
+ * grouping them.
12
12
  */
13
13
  export const BASEKIT_CHART_SLOTS = 5
14
14
 
15
- /** Farbe für Reihe `index` (0-basiert). Ab Slot 6 die zurückgenommene Graustufe. */
15
+ /** Colour for series `index`, zero-based. From slot 6 on, the muted grey. */
16
16
  export function baseKitChartColor(index: number): string {
17
17
  return index < BASEKIT_CHART_SLOTS
18
18
  ? `var(--basekit-chart-${index + 1})`
@@ -33,7 +33,7 @@ export function useChartFormat(): {
33
33
  return new Intl.NumberFormat(locale.value).format(value)
34
34
  }
35
35
 
36
- /** Für Werte an Marken und in Kacheln: 12.400 12,4 Tsd. */
36
+ /** For values on ticks and in tiles: 12,400 becomes 12.4K. */
37
37
  function compact(value: number): string {
38
38
  return value < 10000
39
39
  ? number(value)
@@ -41,9 +41,9 @@ export function useChartFormat(): {
41
41
  }
42
42
 
43
43
  /**
44
- * Anteil in Prozent. Abgerundet, solange nicht alles erreicht ist sonst
45
- * würde 9.999 von 10.000 als 100 %" durchgehen und die eine fehlende
46
- * Übersetzung wäre weggerundet.
44
+ * A share in percent, rounded down as long as the whole has not been
45
+ * reached — otherwise 9,999 out of 10,000 would pass as "100%" and the one
46
+ * missing item would be rounded out of sight.
47
47
  */
48
48
  function percent(value: number, total: number): string {
49
49
  if (total <= 0) return '—'
@@ -61,8 +61,8 @@ export function useChartFormat(): {
61
61
  }
62
62
 
63
63
  /**
64
- * Dezimalpräfixe (kB, MB, GB) nicht KiB/MiB. Speicheranbieter rechnen so
65
- * ab, und die Zahl soll zu der auf der Rechnung passen.
64
+ * Decimal prefixes (kB, MB, GB), not KiB/MiB. Storage is billed that way,
65
+ * and the number here should match the one on the invoice.
66
66
  */
67
67
  function bytes(value: number): string {
68
68
  const units = ['B', 'kB', 'MB', 'GB', 'TB']
@@ -76,7 +76,7 @@ export function useChartFormat(): {
76
76
  return `${new Intl.NumberFormat(locale.value, { maximumFractionDigits: digits }).format(size)} ${units[unit]}`
77
77
  }
78
78
 
79
- /** Laufzeit als 14 h 20 min", unter einer Stunde nur Minuten. */
79
+ /** Runtime as "14 h 20 min"; below an hour, minutes only. */
80
80
  function duration(seconds: number): string {
81
81
  const total = Math.max(0, Math.round(seconds / 60))
82
82
  const hours = Math.floor(total / 60)