frappe-ui 1.0.0-rc.2 → 1.0.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "frappe-ui",
3
- "version": "1.0.0-rc.2",
3
+ "version": "1.0.0",
4
4
  "description": "A set of components and utilities for rapid UI development",
5
5
  "engines": {
6
6
  "node": ">=20.19.0"
@@ -50,7 +50,7 @@
50
50
  "test": "vitest --run",
51
51
  "test:coverage": "vitest --run --coverage",
52
52
  "test:cypress": "cypress run --component",
53
- "test:cypress:coverage": "cross-env COVERAGE=true cypress run --component",
53
+ "test:cypress:coverage": "cross-env COVERAGE=true cypress run --component && nyc report",
54
54
  "coverage": "yarn test:coverage && yarn test:cypress:coverage && yarn coverage:merge",
55
55
  "coverage:merge": "tsx .github/scripts/merge-coverage.ts",
56
56
  "type-check": "vue-tsc --noEmit -p tsconfig.app.json && vue-tsc --noEmit -p tsconfig.node.json",
@@ -310,7 +310,6 @@
310
310
  "@codemirror/lang-xml": "^6.1.0",
311
311
  "@codemirror/lang-yaml": "^6.1.3",
312
312
  "@codemirror/lint": "^6.0.0",
313
- "@cypress/code-coverage": "^3.13.10",
314
313
  "@shikijs/transformers": "^3.15.0",
315
314
  "@vitejs/plugin-vue": "^6.0.7",
316
315
  "@vitest/coverage-v8": "^3.2.4",
package/readme.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  # Frappe UI
6
6
 
7
- **Vue 3 components, design tokens and data composables for Frappe apps**
7
+ **The all-in-one toolkit for modern frontends. High-quality components, app recipes and AI-ready docs.**
8
8
 
9
9
  <a href="https://www.npmjs.com/package/frappe-ui"><img alt="npm version" src="https://img.shields.io/npm/v/frappe-ui.svg?style=flat" /></a>
10
10
  <a href="https://www.npmjs.com/package/frappe-ui"><img alt="npm downloads" src="https://img.shields.io/npm/dm/frappe-ui.svg?style=flat" /></a>
@@ -121,10 +121,9 @@
121
121
  :dir="dir"
122
122
  >
123
123
  <template #default="slotProps">
124
+ <!-- The value item carries the stage's name, so a header would print
125
+ it twice. -->
124
126
  <slot name="tooltip" v-bind="slotProps">
125
- <div class="mb-2 text-p-sm text-ink-gray-5">
126
- {{ slotProps.label }}
127
- </div>
128
127
  <div class="flex flex-col gap-1.5 text-p-sm">
129
128
  <div
130
129
  v-for="item in slotProps.items.filter(
@@ -170,7 +169,7 @@
170
169
 
171
170
  <script setup lang="ts">
172
171
  import { computed, reactive, ref } from 'vue'
173
- import { formatLabel, formatPercent, formatValue } from './format'
172
+ import { formatPercent, formatValue } from './format'
174
173
  import { buildFunnelStages, funnelShapes } from './funnelGeometry'
175
174
  import { useTooltipDismiss } from './core/useTooltipDismiss'
176
175
  import { paletteColors, useChartTokens } from './tokens'
@@ -290,7 +289,9 @@ function readStage(stage: FunnelStage) {
290
289
  tooltip.items = [
291
290
  {
292
291
  name: String(stage.index),
293
- label: formatLabel(props.value),
292
+ // `value` is a row key, often one the caller named when reshaping its
293
+ // rows, so it says nothing to the reader.
294
+ label: stage.label,
294
295
  color: colors.value[stage.index],
295
296
  value: stage.value,
296
297
  formattedValue: formatMeasure(stage.value),
@@ -12,6 +12,7 @@ import {
12
12
  } from './format'
13
13
  import { CHART_FONT_FAMILY } from './measureText'
14
14
  import { paletteColors, type ChartTokens } from './tokens'
15
+ import type { AxisChartFormatters } from './seriesData'
15
16
  import { mergeDeep } from './utils'
16
17
  import type {
17
18
  AxisChartBaseConfig,
@@ -29,6 +30,8 @@ export type AxisChartOptionContext = {
29
30
  hiddenSeries?: string[]
30
31
  /** Plot width in pixels, once measured. Unset leaves pixel sizes to echarts. */
31
32
  width?: number
33
+ /** What a data label prints in when its series has no `format` of its own. */
34
+ format?: AxisChartFormatters
32
35
  }
33
36
 
34
37
  export const AXIS_LABEL_FONT_SIZE = 11
@@ -18,12 +18,14 @@ import {
18
18
  MARK_Z,
19
19
  type AxisChartOptionContext,
20
20
  } from './axisChartCommon'
21
+ import { seriesFormatter } from './axisFormat'
21
22
  import { buildReferenceLineSeries } from './referenceLines'
22
23
  import { mergeDeep } from './utils'
23
24
  import type {
24
25
  AxisChartConfig,
25
26
  AxisChartSeriesConfig,
26
27
  ChartMark,
28
+ ChartValueFormatter,
27
29
  ChartYAxisConfig,
28
30
  } from './types'
29
31
 
@@ -73,6 +75,8 @@ type SeriesContext = {
73
75
  carriesTip: (entry: PlottedSeries, rowIndex: number, value: number) => boolean
74
76
  /** Whether this area stacks onto another, i.e. reads as a band. */
75
77
  banded: boolean
78
+ /** Prints this series' values: its own format, or its axis'. */
79
+ format?: ChartValueFormatter
76
80
  /**
77
81
  * This series' share of its stack, row by row, on a normalized chart. Set
78
82
  * means the plot draws the share and prints it as a percentage; unset means
@@ -95,7 +99,7 @@ export type StackShares = Map<string, (number | null)[]>
95
99
  */
96
100
  export function buildAxisChartOption(
97
101
  config: AxisChartConfig,
98
- { tokens, hiddenSeries = [], width }: AxisChartOptionContext,
102
+ { tokens, hiddenSeries = [], width, format = {} }: AxisChartOptionContext,
99
103
  ): EChartsCoreOption {
100
104
  const isRTL = config.dir === 'rtl'
101
105
  const horizontal = Boolean(config.horizontal)
@@ -163,6 +167,7 @@ export function buildAxisChartOption(
163
167
  isRTL,
164
168
  color: colors[entry.series.name],
165
169
  yAxisIndex: valueAxisIndex(entry.series, hasSecondary),
170
+ format: seriesFormatter(entry.series, format, horizontal),
166
171
  xValue,
167
172
  carriesTip,
168
173
  banded: isBanded(entry, plotted),
@@ -477,11 +482,7 @@ function buildBarSeries(entry: PlottedSeries, ctx: SeriesContext) {
477
482
  : tokens.dataLabel,
478
483
  fontSize: DATA_LABEL_FONT_SIZE,
479
484
  formatter: (params: any) =>
480
- plottedLabel(
481
- horizontal ? params.value?.[0] : params.value?.[1],
482
- series,
483
- Boolean(ctx.share),
484
- ),
485
+ plottedLabel(horizontal ? params.value?.[0] : params.value?.[1], ctx),
485
486
  },
486
487
  labelLayout: { hideOverlap: true },
487
488
  }
@@ -532,8 +533,7 @@ function buildLineSeries(
532
533
  position: 'top',
533
534
  color: tokens.dataLabel,
534
535
  fontSize: DATA_LABEL_FONT_SIZE,
535
- formatter: (params: any) =>
536
- plottedLabel(params.value?.[1], series, Boolean(ctx.share)),
536
+ formatter: (params: any) => plottedLabel(params.value?.[1], ctx),
537
537
  },
538
538
  labelLayout: { hideOverlap: true },
539
539
  }
@@ -550,14 +550,10 @@ function buildLineSeries(
550
550
  * What a data label prints. A normalized series plots a share, so printing it
551
551
  * as a number would read as a count of something.
552
552
  */
553
- function plottedLabel(
554
- value: any,
555
- series: AxisChartSeriesConfig,
556
- normalized: boolean,
557
- ) {
553
+ function plottedLabel(value: any, ctx: SeriesContext) {
558
554
  if (value === null || value === undefined || isNaN(value)) return ''
559
- if (normalized) return formatPercent(value)
560
- return series.format ? series.format(value) : formatValue(value, 1, true)
555
+ if (ctx.share) return formatPercent(value)
556
+ return ctx.format ? ctx.format(value) : formatValue(value, 1, true)
561
557
  }
562
558
 
563
559
  /**
@@ -1,5 +1,10 @@
1
1
  import type { AxisChartFormatters } from './seriesData'
2
- import type { AxisChartBaseConfig, EchartOptionsOverride } from './types'
2
+ import type {
3
+ AxisChartBaseConfig,
4
+ AxisChartSeriesConfig,
5
+ ChartValueFormatter,
6
+ EchartOptionsOverride,
7
+ } from './types'
3
8
  import { mergeDeep } from './utils'
4
9
 
5
10
  /**
@@ -48,3 +53,18 @@ function withFormatter(
48
53
  echartOptions,
49
54
  )
50
55
  }
56
+
57
+ /**
58
+ * How a series prints its values. Without its own format, a series reads in the
59
+ * units of the axis it is actually drawn against, so `y2` series never fall back
60
+ * to the primary formatter — except on a horizontal chart, which has no second
61
+ * axis to put them on.
62
+ */
63
+ export function seriesFormatter(
64
+ series: AxisChartSeriesConfig,
65
+ format: AxisChartFormatters,
66
+ horizontal: boolean,
67
+ ): ChartValueFormatter | undefined {
68
+ const secondary = series.axis === 'y2' && !horizontal
69
+ return series.format ?? (secondary ? format.y2 : format.y)
70
+ }
@@ -11,7 +11,7 @@ import {
11
11
  resolveXAxis,
12
12
  toNumber,
13
13
  } from '../axisChartCommon'
14
- import { applyAxisFormatters } from '../axisFormat'
14
+ import { applyAxisFormatters, seriesFormatter } from '../axisFormat'
15
15
  import { pruneHiddenSeries, toggleHiddenSeries } from '../hiddenSeries'
16
16
  import { buildTooltipItems } from '../tooltipItems'
17
17
  import {
@@ -136,6 +136,7 @@ export function useAxisChart<C extends AxisChartConfig>(
136
136
  tokens: tokens.value,
137
137
  hiddenSeries: hiddenSeries.value,
138
138
  width: plotWidth.value,
139
+ format: format.value,
139
140
  },
140
141
  ),
141
142
  error: null as string | null,
@@ -172,14 +173,8 @@ export function useAxisChart<C extends AxisChartConfig>(
172
173
  })),
173
174
  )
174
175
 
175
- // Without its own format, a series reads in the units of the axis it is
176
- // actually drawn against, so `y2` series never fall back to the primary
177
- // formatter — except on a horizontal chart, which has no second axis to put
178
- // them on.
179
176
  function formatSeriesValue(series: AxisChartSeriesConfig, value: number) {
180
- const secondary = series.axis === 'y2' && !horizontal.value
181
- const formatter =
182
- series.format ?? (secondary ? format.value.y2 : format.value.y)
177
+ const formatter = seriesFormatter(series, format.value, horizontal.value)
183
178
  return formatter ? formatter(value) : formatValue(value)
184
179
  }
185
180
 
@@ -88,9 +88,9 @@ export type AxisChartSeriesConfig = {
88
88
  axis?: 'y' | 'y2'
89
89
  /**
90
90
  * Prints this series' values in the tooltip and its data labels, for a series
91
- * whose unit differs from the others on its axis. Without one, the tooltip
92
- * uses its axis' `format` and a label prints a compact number. A label on a
93
- * normalized stack prints the series' share either way.
91
+ * whose unit differs from the others on its axis. Without one, both use its
92
+ * axis' `format`. A label on a normalized stack prints the series' share
93
+ * either way.
94
94
  */
95
95
  format?: ChartValueFormatter
96
96
  showDataLabels?: boolean
@@ -734,9 +734,9 @@ export type SeriesStyle = {
734
734
  type?: ChartMark
735
735
  /**
736
736
  * Prints this series' values in the tooltip and its data labels, for a series
737
- * whose unit differs from the others on its axis. Without one, the tooltip
738
- * uses its axis' `format` and a label prints a compact number. A label on a
739
- * normalized stack prints the series' share either way.
737
+ * whose unit differs from the others on its axis. Without one, both use its
738
+ * axis' `format`. A label on a normalized stack prints the series' share
739
+ * either way.
740
740
  */
741
741
  format?: ChartValueFormatter
742
742
  /** Prints this series' value beside each of its marks. */
@@ -31,7 +31,7 @@ const themeIcon = computed(
31
31
  <Button
32
32
  v-if="githubUrl"
33
33
  variant="ghost"
34
- :link="githubUrl"
34
+ :href="githubUrl"
35
35
  aria-label="GitHub repository"
36
36
  >
37
37
  <template #icon>