@apipass/design-system 5.8.0 → 6.1.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/dist/lib/charts.index.cjs +1 -1
- package/dist/lib/charts.index.js +11 -8
- package/dist/lib/charts.index.js.map +1 -1
- package/dist/lib/embedded-styles.js +2 -2
- package/dist/lib/lib.index.cjs +1 -1
- package/dist/lib/lib.index.js +183 -182
- package/dist/lib/style.css +1 -1
- package/dist/lib/ui/components/DataTable/DataTable.cjs +1 -1
- package/dist/lib/ui/components/DataTable/DataTable.cjs.map +1 -1
- package/dist/lib/ui/components/DataTable/DataTable.js +157 -124
- package/dist/lib/ui/components/DataTable/DataTable.js.map +1 -1
- package/dist/lib/ui/components/charts/BarChart/bar-chart-option.cjs +1 -1
- package/dist/lib/ui/components/charts/BarChart/bar-chart-option.cjs.map +1 -1
- package/dist/lib/ui/components/charts/BarChart/bar-chart-option.js +75 -104
- package/dist/lib/ui/components/charts/BarChart/bar-chart-option.js.map +1 -1
- package/dist/lib/ui/components/charts/ChartLegend/ChartLegend.cjs +1 -1
- package/dist/lib/ui/components/charts/ChartLegend/ChartLegend.cjs.map +1 -1
- package/dist/lib/ui/components/charts/ChartLegend/ChartLegend.js +27 -26
- package/dist/lib/ui/components/charts/ChartLegend/ChartLegend.js.map +1 -1
- package/dist/lib/ui/components/charts/PieChart/PieChart.cjs +2 -0
- package/dist/lib/ui/components/charts/PieChart/PieChart.cjs.map +1 -0
- package/dist/lib/ui/components/charts/PieChart/PieChart.js +177 -0
- package/dist/lib/ui/components/charts/PieChart/PieChart.js.map +1 -0
- package/dist/lib/ui/components/charts/PieChart/pie-chart-option.cjs +2 -0
- package/dist/lib/ui/components/charts/PieChart/pie-chart-option.cjs.map +1 -0
- package/dist/lib/ui/components/charts/PieChart/pie-chart-option.js +124 -0
- package/dist/lib/ui/components/charts/PieChart/pie-chart-option.js.map +1 -0
- package/dist/lib/ui/components/charts/PieChart/pie-chart-slices.cjs +2 -0
- package/dist/lib/ui/components/charts/PieChart/pie-chart-slices.cjs.map +1 -0
- package/dist/lib/ui/components/charts/PieChart/pie-chart-slices.js +65 -0
- package/dist/lib/ui/components/charts/PieChart/pie-chart-slices.js.map +1 -0
- package/dist/lib/ui/components/charts/chart-color-tone.cjs +2 -0
- package/dist/lib/ui/components/charts/chart-color-tone.cjs.map +1 -0
- package/dist/lib/ui/components/charts/chart-color-tone.js +14 -0
- package/dist/lib/ui/components/charts/chart-color-tone.js.map +1 -0
- package/dist/lib/ui/components/charts/chart-label-color.cjs +2 -0
- package/dist/lib/ui/components/charts/chart-label-color.cjs.map +1 -0
- package/dist/lib/ui/components/charts/chart-label-color.js +35 -0
- package/dist/lib/ui/components/charts/chart-label-color.js.map +1 -0
- package/dist/lib/ui/components/charts/chart-palette.cjs +1 -1
- package/dist/lib/ui/components/charts/chart-palette.cjs.map +1 -1
- package/dist/lib/ui/components/charts/chart-palette.js +2 -1
- package/dist/lib/ui/components/charts/chart-palette.js.map +1 -1
- package/dist/lib/ui/components/charts/chart-theme.cjs +1 -1
- package/dist/lib/ui/components/charts/chart-theme.cjs.map +1 -1
- package/dist/lib/ui/components/charts/chart-theme.js +4 -3
- package/dist/lib/ui/components/charts/chart-theme.js.map +1 -1
- package/dist/lib/ui/components/charts/echarts-modules.cjs +1 -1
- package/dist/lib/ui/components/charts/echarts-modules.cjs.map +1 -1
- package/dist/lib/ui/components/charts/echarts-modules.js +11 -9
- package/dist/lib/ui/components/charts/echarts-modules.js.map +1 -1
- package/dist/lib/ui/primitives/Accordion/Accordion.cjs +1 -1
- package/dist/lib/ui/primitives/Accordion/Accordion.cjs.map +1 -1
- package/dist/lib/ui/primitives/Accordion/Accordion.js +26 -20
- package/dist/lib/ui/primitives/Accordion/Accordion.js.map +1 -1
- package/dist/types/charts.index.d.ts +1 -0
- package/dist/types/ui/components/DataTable/DataTable.d.ts +15 -2
- package/dist/types/ui/components/charts/ChartLegend/ChartLegend.d.ts +1 -0
- package/dist/types/ui/components/charts/PieChart/PieChart.d.ts +32 -0
- package/dist/types/ui/components/charts/PieChart/pie-chart-option.d.ts +13 -0
- package/dist/types/ui/components/charts/PieChart/pie-chart-slices.d.ts +30 -0
- package/dist/types/ui/components/charts/chart-color-tone.d.ts +1 -0
- package/dist/types/ui/components/charts/chart-label-color.d.ts +2 -0
- package/dist/types/ui/components/charts/chart-palette.d.ts +1 -0
- package/dist/types/ui/components/charts/chart-theme.d.ts +1 -0
- package/dist/types/ui/primitives/Accordion/Accordion.d.ts +4 -1
- package/package.json +1 -1
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const v=require("../chart-label-color.cjs"),x={top:16,right:16,bottom:16,left:16},L={vertical:140,horizontal:200},p=48,A="total",s=100,T=new Intl.NumberFormat(void 0,{style:"percent",maximumFractionDigits:1});function _(e){return T.format(e/s)}function w(e){return e.stacking==="percent"?_:e.valueFormatter}function c(e){const o=w(e);if(o)return t=>t===null?"":o(t)}function O(e,o){const t=new Map(e.points.map(r=>[r.category,r.value]));return o.map(r=>t.get(r)??null)}function S(e,o){const t=Array.from({length:o},(r,n)=>e.reduce((l,a)=>l+(a[n]??0),0));return e.map(r=>r.map((n,l)=>{const a=t[l];return n===null||a===0?null:n/a*s}))}function h(e){return e.stacking!=="off"?"inside":e.orientation==="horizontal"?"right":"top"}function P(e){return e.labelRect.width<=e.rect.width&&e.labelRect.height<=e.rect.height}function F(e){const o=e.valueLabel==="auto";return!o||h(e)!=="inside"?{hideOverlap:o}:t=>({hideOverlap:o,fontSize:P(t)?void 0:0})}function V(e,o){const t=h(e),r=c(e);return{show:e.valueLabel!=="never",position:t,color:t==="inside"?v.labelColorOnFill(o):e.theme.labelColor,formatter:r?n=>r(n.value):void 0}}function k(e){const{categories:o,theme:t,orientation:r}=e;return{type:"category",data:o,inverse:r==="horizontal",axisLine:{lineStyle:{color:t.axisLineColor}},axisTick:{show:!1},axisLabel:{color:t.labelColor,hideOverlap:!0,width:L[r],overflow:"truncate"},splitLine:{show:!1}}}function B(e){const{valueAxis:o,stacking:t,theme:r}=e,n=t==="percent",l=c(e);return{type:o.scale==="log"?"log":"value",scale:!n&&o.includeZero===!1,min:n?0:o.min,max:n?s:o.max,axisLine:{show:!1},axisLabel:{color:r.labelColor,formatter:l},splitLine:{lineStyle:{color:r.splitLineColor}}}}function E(e){const{categories:o,series:t,orientation:r,stacking:n,theme:l}=e,a=r==="horizontal",b=c(e),u=t.map(i=>O(i,o)),C=n==="percent"?S(u,o.length):u,y=F(e),f=k(e),m=B(e);return{animation:!1,color:l.seriesColors,textStyle:{fontFamily:l.fontFamily,color:l.labelColor},grid:{...x,containLabel:!0},tooltip:{trigger:"axis",axisPointer:{type:"shadow"},backgroundColor:l.tooltipBackgroundColor,borderColor:l.tooltipBorderColor,textStyle:{color:l.labelColor},valueFormatter:b},xAxis:a?m:f,yAxis:a?f:m,series:t.map((i,g)=>{const d=e.seriesColorOverrides[i.id]??l.seriesColors[g%l.seriesColors.length];return{id:i.id,name:i.name,type:"bar",stack:n==="off"?void 0:A,barMaxWidth:p,data:C[g],itemStyle:{color:d},label:V(e,d),labelLayout:y}})}}exports.buildBarChartOption=E;
|
|
2
2
|
//# sourceMappingURL=bar-chart-option.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bar-chart-option.cjs","sources":["../../../../../../src/ui/components/charts/BarChart/bar-chart-option.ts"],"sourcesContent":["import type { ChartTheme } from \"../chart-theme\";\n\nexport interface BarChartPoint {\n category: string;\n value: number | null;\n}\n\nexport interface BarChartSeriesData {\n id: string;\n name: string;\n points: BarChartPoint[];\n // Nome de token, nunca hex: é o que mantém a cor válida nos dois temas. Sem ele a série\n // recebe a próxima cor da paleta.\n colorToken?: string;\n}\n\nexport type BarChartOrientation = \"vertical\" | \"horizontal\";\n\nexport type BarChartStacking = \"off\" | \"stacked\" | \"percent\";\n\nexport type BarChartValueLabel = \"auto\" | \"always\" | \"never\";\n\nexport type BarChartLegendMode = \"inline\" | \"none\";\n\nexport type BarChartAxisScale = \"linear\" | \"log\";\n\nexport interface BarChartValueAxisOptions {\n scale?: BarChartAxisScale;\n min?: number;\n max?: number;\n includeZero?: boolean;\n}\n\nexport interface BarChartOptionInput {\n categories: string[];\n series: BarChartSeriesData[];\n orientation: BarChartOrientation;\n stacking: BarChartStacking;\n valueLabel: BarChartValueLabel;\n valueAxis: BarChartValueAxisOptions;\n theme: ChartTheme;\n seriesColorOverrides: Record<string, string>;\n valueFormatter?: (value: number) => string;\n}\n\n// A legenda saiu do canvas e virou HTML (`ChartLegend`), entao a grade nao reserva mais faixa\n// para ela: quem divide a altura agora e o flex do componente.\nconst GRID_PADDING = { top: 16, right: 16, bottom: 16, left: 16 } as const;\n\n// Na horizontal o rótulo da categoria disputa largura com a própria barra; na vertical ele fica\n// sob a barra e uma faixa mais estreita já dá conta.\nconst CATEGORY_LABEL_MAX_WIDTH_PX: Record<BarChartOrientation, number> = {\n vertical: 140,\n horizontal: 200,\n};\n\nconst BAR_MAX_WIDTH_PX: number = 48;\n\nconst STACK_ID: string = \"total\";\n\nconst PERCENT_SCALE: number = 100;\n\n// O rótulo dentro da barra é lido contra a cor da série, não contra o fundo da página: a paleta\n// de séries é a mesma nos dois temas, então quem decide entre texto claro e escuro é o\n// preenchimento. O branco que o ECharts usa por padrão reprova no contraste em toda a paleta —\n// 2,24:1 sobre a `yellow-500`.\nconst LABEL_ON_DARK_FILL: string = \"#ffffff\";\n\nconst LABEL_ON_LIGHT_FILL: string = \"#1e1e1e\";\n\nconst LABEL_ON_LIGHT_FILL_LUMINANCE: number = 0.0144;\n\nconst WHITE_LUMINANCE: number = 1;\n\nfunction channelLuminance(channel: number): number {\n const normalized: number = channel / 255;\n return normalized <= 0.03928\n ? normalized / 12.92\n : Math.pow((normalized + 0.055) / 1.055, 2.4);\n}\n\nfunction parseColorChannels(color: string): [number, number, number] | undefined {\n const hexMatch: RegExpMatchArray | null = color\n .trim()\n .match(/^#([0-9a-f]{3}|[0-9a-f]{6})$/i);\n if (hexMatch) {\n const digits: string = hexMatch[1];\n const expanded: string =\n digits.length === 3\n ? digits\n .split(\"\")\n .map((digit: string) => digit + digit)\n .join(\"\")\n : digits;\n return [0, 2, 4].map((start: number) => parseInt(expanded.slice(start, start + 2), 16)) as [\n number,\n number,\n number,\n ];\n }\n\n const rgbMatch: RegExpMatchArray | null = color\n .trim()\n .match(/^rgba?\\(\\s*(\\d+)[\\s,]+(\\d+)[\\s,]+(\\d+)/i);\n if (rgbMatch) {\n return [Number(rgbMatch[1]), Number(rgbMatch[2]), Number(rgbMatch[3])];\n }\n\n return undefined;\n}\n\nfunction relativeLuminance(color: string): number | undefined {\n const channels: [number, number, number] | undefined = parseColorChannels(color);\n if (!channels) return undefined;\n const [red, green, blue] = channels.map(channelLuminance);\n return 0.2126 * red + 0.7152 * green + 0.0722 * blue;\n}\n\nfunction contrastRatio(lighter: number, darker: number): number {\n return (lighter + 0.05) / (darker + 0.05);\n}\n\nfunction labelColorOnFill(fillColor: string): string {\n const luminance: number | undefined = relativeLuminance(fillColor);\n if (luminance === undefined) return LABEL_ON_DARK_FILL;\n const againstLightText: number = contrastRatio(WHITE_LUMINANCE, luminance);\n const againstDarkText: number = contrastRatio(luminance, LABEL_ON_LIGHT_FILL_LUMINANCE);\n return againstDarkText >= againstLightText ? LABEL_ON_LIGHT_FILL : LABEL_ON_DARK_FILL;\n}\n\nconst PERCENT_LABEL: Intl.NumberFormat = new Intl.NumberFormat(undefined, {\n style: \"percent\",\n maximumFractionDigits: 1,\n});\n\nfunction formatPercent(value: number): string {\n return PERCENT_LABEL.format(value / PERCENT_SCALE);\n}\n\n// Em `percent` o valor desenhado é a participação na categoria, e não mais o dado do consumidor:\n// aplicar ali o formatador dele escreveria \"31 ms\" numa barra que vale 31%.\nfunction resolveValueFormatter(input: BarChartOptionInput): ((value: number) => string) | undefined {\n return input.stacking === \"percent\" ? formatPercent : input.valueFormatter;\n}\n\n// Categoria sem valor chega ao formatter como `null` — o ECharts formata o vazio junto com o\n// resto. O tipo público promete `number` ao consumidor, e é esta guarda que mantém a promessa.\nfunction nullSafeValueFormatter(\n input: BarChartOptionInput,\n): ((value: number | null) => string) | undefined {\n const formatValue: ((value: number) => string) | undefined = resolveValueFormatter(input);\n if (!formatValue) return undefined;\n return (value: number | null) => (value === null ? \"\" : formatValue(value));\n}\n\nfunction alignValuesToCategories(\n data: BarChartSeriesData,\n categories: string[],\n): Array<number | null> {\n const valueByCategory: Map<string, number | null> = new Map(\n data.points.map((point: BarChartPoint) => [point.category, point.value]),\n );\n return categories.map((category: string) => valueByCategory.get(category) ?? null);\n}\n\nfunction toShareOfCategoryTotal(\n valuesBySeries: Array<Array<number | null>>,\n categoryCount: number,\n): Array<Array<number | null>> {\n const categoryTotals: number[] = Array.from(\n { length: categoryCount },\n (_unused, categoryIndex: number) =>\n valuesBySeries.reduce(\n (total: number, values: Array<number | null>) => total + (values[categoryIndex] ?? 0),\n 0,\n ),\n );\n return valuesBySeries.map((values: Array<number | null>) =>\n values.map((value: number | null, categoryIndex: number) => {\n const categoryTotal: number = categoryTotals[categoryIndex];\n if (value === null || categoryTotal === 0) return null;\n return (value / categoryTotal) * PERCENT_SCALE;\n }),\n );\n}\n\nfunction labelPosition(input: BarChartOptionInput): string {\n if (input.stacking !== \"off\") return \"inside\";\n return input.orientation === \"horizontal\" ? \"right\" : \"top\";\n}\n\ninterface LabelLayoutParams {\n rect: { width: number; height: number };\n labelRect: { width: number; height: number };\n}\n\ninterface LabelLayout {\n hideOverlap: boolean;\n fontSize?: number;\n}\n\ntype SeriesLabelLayout = LabelLayout | ((params: LabelLayoutParams) => LabelLayout);\n\nfunction fitsInsideBar(params: LabelLayoutParams): boolean {\n return (\n params.labelRect.width <= params.rect.width && params.labelRect.height <= params.rect.height\n );\n}\n\n// `hideOverlap` resolve rótulo que encosta em rótulo, mas não rótulo maior que a própria fatia:\n// numa barra empilhada o ECharts desenha o número por cima das fatias vizinhas. `fontSize: 0` é\n// o que o `labelLayout` oferece para sumir com um rótulo caso a caso.\nfunction seriesLabelLayout(input: BarChartOptionInput): SeriesLabelLayout {\n const hideOverlap: boolean = input.valueLabel === \"auto\";\n if (!hideOverlap || labelPosition(input) !== \"inside\") return { hideOverlap };\n return (params: LabelLayoutParams) => ({\n hideOverlap,\n fontSize: fitsInsideBar(params) ? undefined : 0,\n });\n}\n\nfunction seriesLabel(input: BarChartOptionInput, fillColor: string): Record<string, unknown> {\n const position: string = labelPosition(input);\n const formatValue: ((value: number | null) => string) | undefined = nullSafeValueFormatter(input);\n return {\n show: input.valueLabel !== \"never\",\n position,\n color: position === \"inside\" ? labelColorOnFill(fillColor) : input.theme.labelColor,\n formatter: formatValue\n ? (params: { value: number | null }) => formatValue(params.value)\n : undefined,\n };\n}\n\nfunction categoryAxis(input: BarChartOptionInput): Record<string, unknown> {\n const { categories, theme, orientation } = input;\n return {\n type: \"category\",\n data: categories,\n // Na horizontal o ECharts empilha a categoria de baixo para cima, invertendo a ordem que o\n // app escolheu ao ordenar as barras.\n inverse: orientation === \"horizontal\",\n axisLine: { lineStyle: { color: theme.axisLineColor } },\n axisTick: { show: false },\n axisLabel: {\n color: theme.labelColor,\n hideOverlap: true,\n width: CATEGORY_LABEL_MAX_WIDTH_PX[orientation],\n overflow: \"truncate\",\n },\n splitLine: { show: false },\n };\n}\n\nfunction valueAxis(input: BarChartOptionInput): Record<string, unknown> {\n const { valueAxis: options, stacking, theme } = input;\n const isPercent: boolean = stacking === \"percent\";\n const formatValue: ((value: number | null) => string) | undefined = nullSafeValueFormatter(input);\n return {\n type: options.scale === \"log\" ? \"log\" : \"value\",\n // Barra mede por comprimento: eixo cortado mente sobre a proporção entre as barras, então\n // aqui o zero entra por padrão — ao contrário da série temporal, que só mostra variação.\n scale: !isPercent && options.includeZero === false,\n min: isPercent ? 0 : options.min,\n max: isPercent ? PERCENT_SCALE : options.max,\n axisLine: { show: false },\n axisLabel: { color: theme.labelColor, formatter: formatValue },\n splitLine: { lineStyle: { color: theme.splitLineColor } },\n };\n}\n\n// O retorno é `Record<string, unknown>` de propósito, e não `EChartsOption`: é o que impede\n// um tipo da biblioteca de vazar para o resto do design system.\nexport function buildBarChartOption(input: BarChartOptionInput): Record<string, unknown> {\n const { categories, series, orientation, stacking, theme } = input;\n const isHorizontal: boolean = orientation === \"horizontal\";\n const formatValue: ((value: number | null) => string) | undefined = nullSafeValueFormatter(input);\n\n const alignedValues: Array<Array<number | null>> = series.map((data: BarChartSeriesData) =>\n alignValuesToCategories(data, categories),\n );\n // O ECharts não tem empilhamento 100%: a participação precisa chegar calculada no dado.\n const plottedValues: Array<Array<number | null>> =\n stacking === \"percent\"\n ? toShareOfCategoryTotal(alignedValues, categories.length)\n : alignedValues;\n\n const labelLayout: SeriesLabelLayout = seriesLabelLayout(input);\n const categoryAxisOption: Record<string, unknown> = categoryAxis(input);\n const valueAxisOption: Record<string, unknown> = valueAxis(input);\n\n return {\n animation: false,\n color: theme.seriesColors,\n textStyle: { fontFamily: theme.fontFamily, color: theme.labelColor },\n grid: {\n ...GRID_PADDING,\n containLabel: true,\n },\n tooltip: {\n trigger: \"axis\",\n axisPointer: { type: \"shadow\" },\n backgroundColor: theme.tooltipBackgroundColor,\n borderColor: theme.tooltipBorderColor,\n textStyle: { color: theme.labelColor },\n valueFormatter: formatValue,\n },\n xAxis: isHorizontal ? valueAxisOption : categoryAxisOption,\n yAxis: isHorizontal ? categoryAxisOption : valueAxisOption,\n series: series.map((data: BarChartSeriesData, index: number) => {\n const fillColor: string =\n input.seriesColorOverrides[data.id] ??\n theme.seriesColors[index % theme.seriesColors.length];\n return {\n id: data.id,\n name: data.name,\n type: \"bar\",\n stack: stacking === \"off\" ? undefined : STACK_ID,\n barMaxWidth: BAR_MAX_WIDTH_PX,\n data: plottedValues[index],\n itemStyle: { color: fillColor },\n label: seriesLabel(input, fillColor),\n labelLayout,\n };\n }),\n };\n}\n"],"names":["GRID_PADDING","CATEGORY_LABEL_MAX_WIDTH_PX","BAR_MAX_WIDTH_PX","STACK_ID","PERCENT_SCALE","LABEL_ON_DARK_FILL","LABEL_ON_LIGHT_FILL","LABEL_ON_LIGHT_FILL_LUMINANCE","WHITE_LUMINANCE","channelLuminance","channel","normalized","parseColorChannels","color","hexMatch","digits","expanded","digit","start","rgbMatch","relativeLuminance","channels","red","green","blue","contrastRatio","lighter","darker","labelColorOnFill","fillColor","luminance","againstLightText","PERCENT_LABEL","formatPercent","value","resolveValueFormatter","input","nullSafeValueFormatter","formatValue","alignValuesToCategories","data","categories","valueByCategory","point","category","toShareOfCategoryTotal","valuesBySeries","categoryCount","categoryTotals","_unused","categoryIndex","total","values","categoryTotal","labelPosition","fitsInsideBar","params","seriesLabelLayout","hideOverlap","seriesLabel","position","categoryAxis","theme","orientation","valueAxis","options","stacking","isPercent","buildBarChartOption","series","isHorizontal","alignedValues","plottedValues","labelLayout","categoryAxisOption","valueAxisOption","index"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"bar-chart-option.cjs","sources":["../../../../../../src/ui/components/charts/BarChart/bar-chart-option.ts"],"sourcesContent":["import { labelColorOnFill } from \"../chart-label-color\";\nimport type { ChartTheme } from \"../chart-theme\";\n\nexport interface BarChartPoint {\n category: string;\n value: number | null;\n}\n\nexport interface BarChartSeriesData {\n id: string;\n name: string;\n points: BarChartPoint[];\n // Nome de token, nunca hex: é o que mantém a cor válida nos dois temas. Sem ele a série\n // recebe a próxima cor da paleta.\n colorToken?: string;\n}\n\nexport type BarChartOrientation = \"vertical\" | \"horizontal\";\n\nexport type BarChartStacking = \"off\" | \"stacked\" | \"percent\";\n\nexport type BarChartValueLabel = \"auto\" | \"always\" | \"never\";\n\nexport type BarChartLegendMode = \"inline\" | \"none\";\n\nexport type BarChartAxisScale = \"linear\" | \"log\";\n\nexport interface BarChartValueAxisOptions {\n scale?: BarChartAxisScale;\n min?: number;\n max?: number;\n includeZero?: boolean;\n}\n\nexport interface BarChartOptionInput {\n categories: string[];\n series: BarChartSeriesData[];\n orientation: BarChartOrientation;\n stacking: BarChartStacking;\n valueLabel: BarChartValueLabel;\n valueAxis: BarChartValueAxisOptions;\n theme: ChartTheme;\n seriesColorOverrides: Record<string, string>;\n valueFormatter?: (value: number) => string;\n}\n\n// A legenda saiu do canvas e virou HTML (`ChartLegend`), entao a grade nao reserva mais faixa\n// para ela: quem divide a altura agora e o flex do componente.\nconst GRID_PADDING = { top: 16, right: 16, bottom: 16, left: 16 } as const;\n\n// Na horizontal o rótulo da categoria disputa largura com a própria barra; na vertical ele fica\n// sob a barra e uma faixa mais estreita já dá conta.\nconst CATEGORY_LABEL_MAX_WIDTH_PX: Record<BarChartOrientation, number> = {\n vertical: 140,\n horizontal: 200,\n};\n\nconst BAR_MAX_WIDTH_PX: number = 48;\n\nconst STACK_ID: string = \"total\";\n\nconst PERCENT_SCALE: number = 100;\n\nconst PERCENT_LABEL: Intl.NumberFormat = new Intl.NumberFormat(undefined, {\n style: \"percent\",\n maximumFractionDigits: 1,\n});\n\nfunction formatPercent(value: number): string {\n return PERCENT_LABEL.format(value / PERCENT_SCALE);\n}\n\n// Em `percent` o valor desenhado é a participação na categoria, e não mais o dado do consumidor:\n// aplicar ali o formatador dele escreveria \"31 ms\" numa barra que vale 31%.\nfunction resolveValueFormatter(input: BarChartOptionInput): ((value: number) => string) | undefined {\n return input.stacking === \"percent\" ? formatPercent : input.valueFormatter;\n}\n\n// Categoria sem valor chega ao formatter como `null` — o ECharts formata o vazio junto com o\n// resto. O tipo público promete `number` ao consumidor, e é esta guarda que mantém a promessa.\nfunction nullSafeValueFormatter(\n input: BarChartOptionInput,\n): ((value: number | null) => string) | undefined {\n const formatValue: ((value: number) => string) | undefined = resolveValueFormatter(input);\n if (!formatValue) return undefined;\n return (value: number | null) => (value === null ? \"\" : formatValue(value));\n}\n\nfunction alignValuesToCategories(\n data: BarChartSeriesData,\n categories: string[],\n): Array<number | null> {\n const valueByCategory: Map<string, number | null> = new Map(\n data.points.map((point: BarChartPoint) => [point.category, point.value]),\n );\n return categories.map((category: string) => valueByCategory.get(category) ?? null);\n}\n\nfunction toShareOfCategoryTotal(\n valuesBySeries: Array<Array<number | null>>,\n categoryCount: number,\n): Array<Array<number | null>> {\n const categoryTotals: number[] = Array.from(\n { length: categoryCount },\n (_unused, categoryIndex: number) =>\n valuesBySeries.reduce(\n (total: number, values: Array<number | null>) => total + (values[categoryIndex] ?? 0),\n 0,\n ),\n );\n return valuesBySeries.map((values: Array<number | null>) =>\n values.map((value: number | null, categoryIndex: number) => {\n const categoryTotal: number = categoryTotals[categoryIndex];\n if (value === null || categoryTotal === 0) return null;\n return (value / categoryTotal) * PERCENT_SCALE;\n }),\n );\n}\n\nfunction labelPosition(input: BarChartOptionInput): string {\n if (input.stacking !== \"off\") return \"inside\";\n return input.orientation === \"horizontal\" ? \"right\" : \"top\";\n}\n\ninterface LabelLayoutParams {\n rect: { width: number; height: number };\n labelRect: { width: number; height: number };\n}\n\ninterface LabelLayout {\n hideOverlap: boolean;\n fontSize?: number;\n}\n\ntype SeriesLabelLayout = LabelLayout | ((params: LabelLayoutParams) => LabelLayout);\n\nfunction fitsInsideBar(params: LabelLayoutParams): boolean {\n return (\n params.labelRect.width <= params.rect.width && params.labelRect.height <= params.rect.height\n );\n}\n\n// `hideOverlap` resolve rótulo que encosta em rótulo, mas não rótulo maior que a própria fatia:\n// numa barra empilhada o ECharts desenha o número por cima das fatias vizinhas. `fontSize: 0` é\n// o que o `labelLayout` oferece para sumir com um rótulo caso a caso.\nfunction seriesLabelLayout(input: BarChartOptionInput): SeriesLabelLayout {\n const hideOverlap: boolean = input.valueLabel === \"auto\";\n if (!hideOverlap || labelPosition(input) !== \"inside\") return { hideOverlap };\n return (params: LabelLayoutParams) => ({\n hideOverlap,\n fontSize: fitsInsideBar(params) ? undefined : 0,\n });\n}\n\nfunction seriesLabel(input: BarChartOptionInput, fillColor: string): Record<string, unknown> {\n const position: string = labelPosition(input);\n const formatValue: ((value: number | null) => string) | undefined = nullSafeValueFormatter(input);\n return {\n show: input.valueLabel !== \"never\",\n position,\n color: position === \"inside\" ? labelColorOnFill(fillColor) : input.theme.labelColor,\n formatter: formatValue\n ? (params: { value: number | null }) => formatValue(params.value)\n : undefined,\n };\n}\n\nfunction categoryAxis(input: BarChartOptionInput): Record<string, unknown> {\n const { categories, theme, orientation } = input;\n return {\n type: \"category\",\n data: categories,\n // Na horizontal o ECharts empilha a categoria de baixo para cima, invertendo a ordem que o\n // app escolheu ao ordenar as barras.\n inverse: orientation === \"horizontal\",\n axisLine: { lineStyle: { color: theme.axisLineColor } },\n axisTick: { show: false },\n axisLabel: {\n color: theme.labelColor,\n hideOverlap: true,\n width: CATEGORY_LABEL_MAX_WIDTH_PX[orientation],\n overflow: \"truncate\",\n },\n splitLine: { show: false },\n };\n}\n\nfunction valueAxis(input: BarChartOptionInput): Record<string, unknown> {\n const { valueAxis: options, stacking, theme } = input;\n const isPercent: boolean = stacking === \"percent\";\n const formatValue: ((value: number | null) => string) | undefined = nullSafeValueFormatter(input);\n return {\n type: options.scale === \"log\" ? \"log\" : \"value\",\n // Barra mede por comprimento: eixo cortado mente sobre a proporção entre as barras, então\n // aqui o zero entra por padrão — ao contrário da série temporal, que só mostra variação.\n scale: !isPercent && options.includeZero === false,\n min: isPercent ? 0 : options.min,\n max: isPercent ? PERCENT_SCALE : options.max,\n axisLine: { show: false },\n axisLabel: { color: theme.labelColor, formatter: formatValue },\n splitLine: { lineStyle: { color: theme.splitLineColor } },\n };\n}\n\n// O retorno é `Record<string, unknown>` de propósito, e não `EChartsOption`: é o que impede\n// um tipo da biblioteca de vazar para o resto do design system.\nexport function buildBarChartOption(input: BarChartOptionInput): Record<string, unknown> {\n const { categories, series, orientation, stacking, theme } = input;\n const isHorizontal: boolean = orientation === \"horizontal\";\n const formatValue: ((value: number | null) => string) | undefined = nullSafeValueFormatter(input);\n\n const alignedValues: Array<Array<number | null>> = series.map((data: BarChartSeriesData) =>\n alignValuesToCategories(data, categories),\n );\n // O ECharts não tem empilhamento 100%: a participação precisa chegar calculada no dado.\n const plottedValues: Array<Array<number | null>> =\n stacking === \"percent\"\n ? toShareOfCategoryTotal(alignedValues, categories.length)\n : alignedValues;\n\n const labelLayout: SeriesLabelLayout = seriesLabelLayout(input);\n const categoryAxisOption: Record<string, unknown> = categoryAxis(input);\n const valueAxisOption: Record<string, unknown> = valueAxis(input);\n\n return {\n animation: false,\n color: theme.seriesColors,\n textStyle: { fontFamily: theme.fontFamily, color: theme.labelColor },\n grid: {\n ...GRID_PADDING,\n containLabel: true,\n },\n tooltip: {\n trigger: \"axis\",\n axisPointer: { type: \"shadow\" },\n backgroundColor: theme.tooltipBackgroundColor,\n borderColor: theme.tooltipBorderColor,\n textStyle: { color: theme.labelColor },\n valueFormatter: formatValue,\n },\n xAxis: isHorizontal ? valueAxisOption : categoryAxisOption,\n yAxis: isHorizontal ? categoryAxisOption : valueAxisOption,\n series: series.map((data: BarChartSeriesData, index: number) => {\n const fillColor: string =\n input.seriesColorOverrides[data.id] ??\n theme.seriesColors[index % theme.seriesColors.length];\n return {\n id: data.id,\n name: data.name,\n type: \"bar\",\n stack: stacking === \"off\" ? undefined : STACK_ID,\n barMaxWidth: BAR_MAX_WIDTH_PX,\n data: plottedValues[index],\n itemStyle: { color: fillColor },\n label: seriesLabel(input, fillColor),\n labelLayout,\n };\n }),\n };\n}\n"],"names":["GRID_PADDING","CATEGORY_LABEL_MAX_WIDTH_PX","BAR_MAX_WIDTH_PX","STACK_ID","PERCENT_SCALE","PERCENT_LABEL","formatPercent","value","resolveValueFormatter","input","nullSafeValueFormatter","formatValue","alignValuesToCategories","data","categories","valueByCategory","point","category","toShareOfCategoryTotal","valuesBySeries","categoryCount","categoryTotals","_unused","categoryIndex","total","values","categoryTotal","labelPosition","fitsInsideBar","params","seriesLabelLayout","hideOverlap","seriesLabel","fillColor","position","labelColorOnFill","categoryAxis","theme","orientation","valueAxis","options","stacking","isPercent","buildBarChartOption","series","isHorizontal","alignedValues","plottedValues","labelLayout","categoryAxisOption","valueAxisOption","index"],"mappings":"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"}
|
|
@@ -1,166 +1,137 @@
|
|
|
1
|
-
|
|
1
|
+
import { labelColorOnFill as x } from "../chart-label-color.js";
|
|
2
|
+
const v = { top: 16, right: 16, bottom: 16, left: 16 }, L = {
|
|
2
3
|
vertical: 140,
|
|
3
4
|
horizontal: 200
|
|
4
|
-
},
|
|
5
|
-
function N(e) {
|
|
6
|
-
const t = e / 255;
|
|
7
|
-
return t <= 0.03928 ? t / 12.92 : Math.pow((t + 0.055) / 1.055, 2.4);
|
|
8
|
-
}
|
|
9
|
-
function w(e) {
|
|
10
|
-
const t = e.trim().match(/^#([0-9a-f]{3}|[0-9a-f]{6})$/i);
|
|
11
|
-
if (t) {
|
|
12
|
-
const r = t[1], a = r.length === 3 ? r.split("").map((n) => n + n).join("") : r;
|
|
13
|
-
return [0, 2, 4].map((n) => parseInt(a.slice(n, n + 2), 16));
|
|
14
|
-
}
|
|
15
|
-
const o = e.trim().match(/^rgba?\(\s*(\d+)[\s,]+(\d+)[\s,]+(\d+)/i);
|
|
16
|
-
if (o)
|
|
17
|
-
return [Number(o[1]), Number(o[2]), Number(o[3])];
|
|
18
|
-
}
|
|
19
|
-
function F(e) {
|
|
20
|
-
const t = w(e);
|
|
21
|
-
if (!t) return;
|
|
22
|
-
const [o, r, a] = t.map(N);
|
|
23
|
-
return 0.2126 * o + 0.7152 * r + 0.0722 * a;
|
|
24
|
-
}
|
|
25
|
-
function L(e, t) {
|
|
26
|
-
return (e + 0.05) / (t + 0.05);
|
|
27
|
-
}
|
|
28
|
-
function O(e) {
|
|
29
|
-
const t = F(e);
|
|
30
|
-
if (t === void 0) return d;
|
|
31
|
-
const o = L(E, t);
|
|
32
|
-
return L(t, I) >= o ? T : d;
|
|
33
|
-
}
|
|
34
|
-
const k = new Intl.NumberFormat(void 0, {
|
|
5
|
+
}, p = 48, A = "total", s = 100, _ = new Intl.NumberFormat(void 0, {
|
|
35
6
|
style: "percent",
|
|
36
7
|
maximumFractionDigits: 1
|
|
37
8
|
});
|
|
38
|
-
function
|
|
39
|
-
return
|
|
9
|
+
function w(o) {
|
|
10
|
+
return _.format(o / s);
|
|
40
11
|
}
|
|
41
|
-
function
|
|
42
|
-
return
|
|
12
|
+
function T(o) {
|
|
13
|
+
return o.stacking === "percent" ? w : o.valueFormatter;
|
|
43
14
|
}
|
|
44
|
-
function c(
|
|
45
|
-
const
|
|
46
|
-
if (
|
|
47
|
-
return (
|
|
15
|
+
function c(o) {
|
|
16
|
+
const e = T(o);
|
|
17
|
+
if (e)
|
|
18
|
+
return (t) => t === null ? "" : e(t);
|
|
48
19
|
}
|
|
49
|
-
function
|
|
50
|
-
const
|
|
51
|
-
|
|
20
|
+
function F(o, e) {
|
|
21
|
+
const t = new Map(
|
|
22
|
+
o.points.map((r) => [r.category, r.value])
|
|
52
23
|
);
|
|
53
|
-
return
|
|
24
|
+
return e.map((r) => t.get(r) ?? null);
|
|
54
25
|
}
|
|
55
|
-
function
|
|
56
|
-
const
|
|
57
|
-
{ length:
|
|
58
|
-
(r,
|
|
59
|
-
(
|
|
26
|
+
function O(o, e) {
|
|
27
|
+
const t = Array.from(
|
|
28
|
+
{ length: e },
|
|
29
|
+
(r, n) => o.reduce(
|
|
30
|
+
(l, a) => l + (a[n] ?? 0),
|
|
60
31
|
0
|
|
61
32
|
)
|
|
62
33
|
);
|
|
63
|
-
return
|
|
64
|
-
(r) => r.map((
|
|
65
|
-
const
|
|
66
|
-
return
|
|
34
|
+
return o.map(
|
|
35
|
+
(r) => r.map((n, l) => {
|
|
36
|
+
const a = t[l];
|
|
37
|
+
return n === null || a === 0 ? null : n / a * s;
|
|
67
38
|
})
|
|
68
39
|
);
|
|
69
40
|
}
|
|
70
|
-
function
|
|
71
|
-
return
|
|
41
|
+
function h(o) {
|
|
42
|
+
return o.stacking !== "off" ? "inside" : o.orientation === "horizontal" ? "right" : "top";
|
|
72
43
|
}
|
|
73
|
-
function
|
|
74
|
-
return
|
|
44
|
+
function P(o) {
|
|
45
|
+
return o.labelRect.width <= o.rect.width && o.labelRect.height <= o.rect.height;
|
|
75
46
|
}
|
|
76
|
-
function S(
|
|
77
|
-
const
|
|
78
|
-
return !
|
|
79
|
-
hideOverlap:
|
|
80
|
-
fontSize:
|
|
47
|
+
function S(o) {
|
|
48
|
+
const e = o.valueLabel === "auto";
|
|
49
|
+
return !e || h(o) !== "inside" ? { hideOverlap: e } : (t) => ({
|
|
50
|
+
hideOverlap: e,
|
|
51
|
+
fontSize: P(t) ? void 0 : 0
|
|
81
52
|
});
|
|
82
53
|
}
|
|
83
|
-
function V(
|
|
84
|
-
const
|
|
54
|
+
function V(o, e) {
|
|
55
|
+
const t = h(o), r = c(o);
|
|
85
56
|
return {
|
|
86
|
-
show:
|
|
87
|
-
position:
|
|
88
|
-
color:
|
|
89
|
-
formatter: r ? (
|
|
57
|
+
show: o.valueLabel !== "never",
|
|
58
|
+
position: t,
|
|
59
|
+
color: t === "inside" ? x(e) : o.theme.labelColor,
|
|
60
|
+
formatter: r ? (n) => r(n.value) : void 0
|
|
90
61
|
};
|
|
91
62
|
}
|
|
92
|
-
function
|
|
93
|
-
const { categories:
|
|
63
|
+
function k(o) {
|
|
64
|
+
const { categories: e, theme: t, orientation: r } = o;
|
|
94
65
|
return {
|
|
95
66
|
type: "category",
|
|
96
|
-
data:
|
|
67
|
+
data: e,
|
|
97
68
|
// Na horizontal o ECharts empilha a categoria de baixo para cima, invertendo a ordem que o
|
|
98
69
|
// app escolheu ao ordenar as barras.
|
|
99
70
|
inverse: r === "horizontal",
|
|
100
|
-
axisLine: { lineStyle: { color:
|
|
71
|
+
axisLine: { lineStyle: { color: t.axisLineColor } },
|
|
101
72
|
axisTick: { show: !1 },
|
|
102
73
|
axisLabel: {
|
|
103
|
-
color:
|
|
74
|
+
color: t.labelColor,
|
|
104
75
|
hideOverlap: !0,
|
|
105
|
-
width:
|
|
76
|
+
width: L[r],
|
|
106
77
|
overflow: "truncate"
|
|
107
78
|
},
|
|
108
79
|
splitLine: { show: !1 }
|
|
109
80
|
};
|
|
110
81
|
}
|
|
111
|
-
function
|
|
112
|
-
const { valueAxis:
|
|
82
|
+
function E(o) {
|
|
83
|
+
const { valueAxis: e, stacking: t, theme: r } = o, n = t === "percent", l = c(o);
|
|
113
84
|
return {
|
|
114
|
-
type:
|
|
85
|
+
type: e.scale === "log" ? "log" : "value",
|
|
115
86
|
// Barra mede por comprimento: eixo cortado mente sobre a proporção entre as barras, então
|
|
116
87
|
// aqui o zero entra por padrão — ao contrário da série temporal, que só mostra variação.
|
|
117
|
-
scale: !
|
|
118
|
-
min:
|
|
119
|
-
max:
|
|
88
|
+
scale: !n && e.includeZero === !1,
|
|
89
|
+
min: n ? 0 : e.min,
|
|
90
|
+
max: n ? s : e.max,
|
|
120
91
|
axisLine: { show: !1 },
|
|
121
|
-
axisLabel: { color: r.labelColor, formatter:
|
|
92
|
+
axisLabel: { color: r.labelColor, formatter: l },
|
|
122
93
|
splitLine: { lineStyle: { color: r.splitLineColor } }
|
|
123
94
|
};
|
|
124
95
|
}
|
|
125
|
-
function
|
|
126
|
-
const { categories:
|
|
127
|
-
(
|
|
128
|
-
),
|
|
96
|
+
function D(o) {
|
|
97
|
+
const { categories: e, series: t, orientation: r, stacking: n, theme: l } = o, a = r === "horizontal", C = c(o), u = t.map(
|
|
98
|
+
(i) => F(i, e)
|
|
99
|
+
), b = n === "percent" ? O(u, e.length) : u, y = S(o), f = k(o), m = E(o);
|
|
129
100
|
return {
|
|
130
101
|
animation: !1,
|
|
131
|
-
color:
|
|
132
|
-
textStyle: { fontFamily:
|
|
102
|
+
color: l.seriesColors,
|
|
103
|
+
textStyle: { fontFamily: l.fontFamily, color: l.labelColor },
|
|
133
104
|
grid: {
|
|
134
|
-
...
|
|
105
|
+
...v,
|
|
135
106
|
containLabel: !0
|
|
136
107
|
},
|
|
137
108
|
tooltip: {
|
|
138
109
|
trigger: "axis",
|
|
139
110
|
axisPointer: { type: "shadow" },
|
|
140
|
-
backgroundColor:
|
|
141
|
-
borderColor:
|
|
142
|
-
textStyle: { color:
|
|
111
|
+
backgroundColor: l.tooltipBackgroundColor,
|
|
112
|
+
borderColor: l.tooltipBorderColor,
|
|
113
|
+
textStyle: { color: l.labelColor },
|
|
143
114
|
valueFormatter: C
|
|
144
115
|
},
|
|
145
|
-
xAxis:
|
|
146
|
-
yAxis:
|
|
147
|
-
series:
|
|
148
|
-
const
|
|
116
|
+
xAxis: a ? m : f,
|
|
117
|
+
yAxis: a ? f : m,
|
|
118
|
+
series: t.map((i, g) => {
|
|
119
|
+
const d = o.seriesColorOverrides[i.id] ?? l.seriesColors[g % l.seriesColors.length];
|
|
149
120
|
return {
|
|
150
|
-
id:
|
|
151
|
-
name:
|
|
121
|
+
id: i.id,
|
|
122
|
+
name: i.name,
|
|
152
123
|
type: "bar",
|
|
153
|
-
stack:
|
|
154
|
-
barMaxWidth:
|
|
155
|
-
data:
|
|
156
|
-
itemStyle: { color:
|
|
157
|
-
label: V(
|
|
158
|
-
labelLayout:
|
|
124
|
+
stack: n === "off" ? void 0 : A,
|
|
125
|
+
barMaxWidth: p,
|
|
126
|
+
data: b[g],
|
|
127
|
+
itemStyle: { color: d },
|
|
128
|
+
label: V(o, d),
|
|
129
|
+
labelLayout: y
|
|
159
130
|
};
|
|
160
131
|
})
|
|
161
132
|
};
|
|
162
133
|
}
|
|
163
134
|
export {
|
|
164
|
-
|
|
135
|
+
D as buildBarChartOption
|
|
165
136
|
};
|
|
166
137
|
//# sourceMappingURL=bar-chart-option.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bar-chart-option.js","sources":["../../../../../../src/ui/components/charts/BarChart/bar-chart-option.ts"],"sourcesContent":["import type { ChartTheme } from \"../chart-theme\";\n\nexport interface BarChartPoint {\n category: string;\n value: number | null;\n}\n\nexport interface BarChartSeriesData {\n id: string;\n name: string;\n points: BarChartPoint[];\n // Nome de token, nunca hex: é o que mantém a cor válida nos dois temas. Sem ele a série\n // recebe a próxima cor da paleta.\n colorToken?: string;\n}\n\nexport type BarChartOrientation = \"vertical\" | \"horizontal\";\n\nexport type BarChartStacking = \"off\" | \"stacked\" | \"percent\";\n\nexport type BarChartValueLabel = \"auto\" | \"always\" | \"never\";\n\nexport type BarChartLegendMode = \"inline\" | \"none\";\n\nexport type BarChartAxisScale = \"linear\" | \"log\";\n\nexport interface BarChartValueAxisOptions {\n scale?: BarChartAxisScale;\n min?: number;\n max?: number;\n includeZero?: boolean;\n}\n\nexport interface BarChartOptionInput {\n categories: string[];\n series: BarChartSeriesData[];\n orientation: BarChartOrientation;\n stacking: BarChartStacking;\n valueLabel: BarChartValueLabel;\n valueAxis: BarChartValueAxisOptions;\n theme: ChartTheme;\n seriesColorOverrides: Record<string, string>;\n valueFormatter?: (value: number) => string;\n}\n\n// A legenda saiu do canvas e virou HTML (`ChartLegend`), entao a grade nao reserva mais faixa\n// para ela: quem divide a altura agora e o flex do componente.\nconst GRID_PADDING = { top: 16, right: 16, bottom: 16, left: 16 } as const;\n\n// Na horizontal o rótulo da categoria disputa largura com a própria barra; na vertical ele fica\n// sob a barra e uma faixa mais estreita já dá conta.\nconst CATEGORY_LABEL_MAX_WIDTH_PX: Record<BarChartOrientation, number> = {\n vertical: 140,\n horizontal: 200,\n};\n\nconst BAR_MAX_WIDTH_PX: number = 48;\n\nconst STACK_ID: string = \"total\";\n\nconst PERCENT_SCALE: number = 100;\n\n// O rótulo dentro da barra é lido contra a cor da série, não contra o fundo da página: a paleta\n// de séries é a mesma nos dois temas, então quem decide entre texto claro e escuro é o\n// preenchimento. O branco que o ECharts usa por padrão reprova no contraste em toda a paleta —\n// 2,24:1 sobre a `yellow-500`.\nconst LABEL_ON_DARK_FILL: string = \"#ffffff\";\n\nconst LABEL_ON_LIGHT_FILL: string = \"#1e1e1e\";\n\nconst LABEL_ON_LIGHT_FILL_LUMINANCE: number = 0.0144;\n\nconst WHITE_LUMINANCE: number = 1;\n\nfunction channelLuminance(channel: number): number {\n const normalized: number = channel / 255;\n return normalized <= 0.03928\n ? normalized / 12.92\n : Math.pow((normalized + 0.055) / 1.055, 2.4);\n}\n\nfunction parseColorChannels(color: string): [number, number, number] | undefined {\n const hexMatch: RegExpMatchArray | null = color\n .trim()\n .match(/^#([0-9a-f]{3}|[0-9a-f]{6})$/i);\n if (hexMatch) {\n const digits: string = hexMatch[1];\n const expanded: string =\n digits.length === 3\n ? digits\n .split(\"\")\n .map((digit: string) => digit + digit)\n .join(\"\")\n : digits;\n return [0, 2, 4].map((start: number) => parseInt(expanded.slice(start, start + 2), 16)) as [\n number,\n number,\n number,\n ];\n }\n\n const rgbMatch: RegExpMatchArray | null = color\n .trim()\n .match(/^rgba?\\(\\s*(\\d+)[\\s,]+(\\d+)[\\s,]+(\\d+)/i);\n if (rgbMatch) {\n return [Number(rgbMatch[1]), Number(rgbMatch[2]), Number(rgbMatch[3])];\n }\n\n return undefined;\n}\n\nfunction relativeLuminance(color: string): number | undefined {\n const channels: [number, number, number] | undefined = parseColorChannels(color);\n if (!channels) return undefined;\n const [red, green, blue] = channels.map(channelLuminance);\n return 0.2126 * red + 0.7152 * green + 0.0722 * blue;\n}\n\nfunction contrastRatio(lighter: number, darker: number): number {\n return (lighter + 0.05) / (darker + 0.05);\n}\n\nfunction labelColorOnFill(fillColor: string): string {\n const luminance: number | undefined = relativeLuminance(fillColor);\n if (luminance === undefined) return LABEL_ON_DARK_FILL;\n const againstLightText: number = contrastRatio(WHITE_LUMINANCE, luminance);\n const againstDarkText: number = contrastRatio(luminance, LABEL_ON_LIGHT_FILL_LUMINANCE);\n return againstDarkText >= againstLightText ? LABEL_ON_LIGHT_FILL : LABEL_ON_DARK_FILL;\n}\n\nconst PERCENT_LABEL: Intl.NumberFormat = new Intl.NumberFormat(undefined, {\n style: \"percent\",\n maximumFractionDigits: 1,\n});\n\nfunction formatPercent(value: number): string {\n return PERCENT_LABEL.format(value / PERCENT_SCALE);\n}\n\n// Em `percent` o valor desenhado é a participação na categoria, e não mais o dado do consumidor:\n// aplicar ali o formatador dele escreveria \"31 ms\" numa barra que vale 31%.\nfunction resolveValueFormatter(input: BarChartOptionInput): ((value: number) => string) | undefined {\n return input.stacking === \"percent\" ? formatPercent : input.valueFormatter;\n}\n\n// Categoria sem valor chega ao formatter como `null` — o ECharts formata o vazio junto com o\n// resto. O tipo público promete `number` ao consumidor, e é esta guarda que mantém a promessa.\nfunction nullSafeValueFormatter(\n input: BarChartOptionInput,\n): ((value: number | null) => string) | undefined {\n const formatValue: ((value: number) => string) | undefined = resolveValueFormatter(input);\n if (!formatValue) return undefined;\n return (value: number | null) => (value === null ? \"\" : formatValue(value));\n}\n\nfunction alignValuesToCategories(\n data: BarChartSeriesData,\n categories: string[],\n): Array<number | null> {\n const valueByCategory: Map<string, number | null> = new Map(\n data.points.map((point: BarChartPoint) => [point.category, point.value]),\n );\n return categories.map((category: string) => valueByCategory.get(category) ?? null);\n}\n\nfunction toShareOfCategoryTotal(\n valuesBySeries: Array<Array<number | null>>,\n categoryCount: number,\n): Array<Array<number | null>> {\n const categoryTotals: number[] = Array.from(\n { length: categoryCount },\n (_unused, categoryIndex: number) =>\n valuesBySeries.reduce(\n (total: number, values: Array<number | null>) => total + (values[categoryIndex] ?? 0),\n 0,\n ),\n );\n return valuesBySeries.map((values: Array<number | null>) =>\n values.map((value: number | null, categoryIndex: number) => {\n const categoryTotal: number = categoryTotals[categoryIndex];\n if (value === null || categoryTotal === 0) return null;\n return (value / categoryTotal) * PERCENT_SCALE;\n }),\n );\n}\n\nfunction labelPosition(input: BarChartOptionInput): string {\n if (input.stacking !== \"off\") return \"inside\";\n return input.orientation === \"horizontal\" ? \"right\" : \"top\";\n}\n\ninterface LabelLayoutParams {\n rect: { width: number; height: number };\n labelRect: { width: number; height: number };\n}\n\ninterface LabelLayout {\n hideOverlap: boolean;\n fontSize?: number;\n}\n\ntype SeriesLabelLayout = LabelLayout | ((params: LabelLayoutParams) => LabelLayout);\n\nfunction fitsInsideBar(params: LabelLayoutParams): boolean {\n return (\n params.labelRect.width <= params.rect.width && params.labelRect.height <= params.rect.height\n );\n}\n\n// `hideOverlap` resolve rótulo que encosta em rótulo, mas não rótulo maior que a própria fatia:\n// numa barra empilhada o ECharts desenha o número por cima das fatias vizinhas. `fontSize: 0` é\n// o que o `labelLayout` oferece para sumir com um rótulo caso a caso.\nfunction seriesLabelLayout(input: BarChartOptionInput): SeriesLabelLayout {\n const hideOverlap: boolean = input.valueLabel === \"auto\";\n if (!hideOverlap || labelPosition(input) !== \"inside\") return { hideOverlap };\n return (params: LabelLayoutParams) => ({\n hideOverlap,\n fontSize: fitsInsideBar(params) ? undefined : 0,\n });\n}\n\nfunction seriesLabel(input: BarChartOptionInput, fillColor: string): Record<string, unknown> {\n const position: string = labelPosition(input);\n const formatValue: ((value: number | null) => string) | undefined = nullSafeValueFormatter(input);\n return {\n show: input.valueLabel !== \"never\",\n position,\n color: position === \"inside\" ? labelColorOnFill(fillColor) : input.theme.labelColor,\n formatter: formatValue\n ? (params: { value: number | null }) => formatValue(params.value)\n : undefined,\n };\n}\n\nfunction categoryAxis(input: BarChartOptionInput): Record<string, unknown> {\n const { categories, theme, orientation } = input;\n return {\n type: \"category\",\n data: categories,\n // Na horizontal o ECharts empilha a categoria de baixo para cima, invertendo a ordem que o\n // app escolheu ao ordenar as barras.\n inverse: orientation === \"horizontal\",\n axisLine: { lineStyle: { color: theme.axisLineColor } },\n axisTick: { show: false },\n axisLabel: {\n color: theme.labelColor,\n hideOverlap: true,\n width: CATEGORY_LABEL_MAX_WIDTH_PX[orientation],\n overflow: \"truncate\",\n },\n splitLine: { show: false },\n };\n}\n\nfunction valueAxis(input: BarChartOptionInput): Record<string, unknown> {\n const { valueAxis: options, stacking, theme } = input;\n const isPercent: boolean = stacking === \"percent\";\n const formatValue: ((value: number | null) => string) | undefined = nullSafeValueFormatter(input);\n return {\n type: options.scale === \"log\" ? \"log\" : \"value\",\n // Barra mede por comprimento: eixo cortado mente sobre a proporção entre as barras, então\n // aqui o zero entra por padrão — ao contrário da série temporal, que só mostra variação.\n scale: !isPercent && options.includeZero === false,\n min: isPercent ? 0 : options.min,\n max: isPercent ? PERCENT_SCALE : options.max,\n axisLine: { show: false },\n axisLabel: { color: theme.labelColor, formatter: formatValue },\n splitLine: { lineStyle: { color: theme.splitLineColor } },\n };\n}\n\n// O retorno é `Record<string, unknown>` de propósito, e não `EChartsOption`: é o que impede\n// um tipo da biblioteca de vazar para o resto do design system.\nexport function buildBarChartOption(input: BarChartOptionInput): Record<string, unknown> {\n const { categories, series, orientation, stacking, theme } = input;\n const isHorizontal: boolean = orientation === \"horizontal\";\n const formatValue: ((value: number | null) => string) | undefined = nullSafeValueFormatter(input);\n\n const alignedValues: Array<Array<number | null>> = series.map((data: BarChartSeriesData) =>\n alignValuesToCategories(data, categories),\n );\n // O ECharts não tem empilhamento 100%: a participação precisa chegar calculada no dado.\n const plottedValues: Array<Array<number | null>> =\n stacking === \"percent\"\n ? toShareOfCategoryTotal(alignedValues, categories.length)\n : alignedValues;\n\n const labelLayout: SeriesLabelLayout = seriesLabelLayout(input);\n const categoryAxisOption: Record<string, unknown> = categoryAxis(input);\n const valueAxisOption: Record<string, unknown> = valueAxis(input);\n\n return {\n animation: false,\n color: theme.seriesColors,\n textStyle: { fontFamily: theme.fontFamily, color: theme.labelColor },\n grid: {\n ...GRID_PADDING,\n containLabel: true,\n },\n tooltip: {\n trigger: \"axis\",\n axisPointer: { type: \"shadow\" },\n backgroundColor: theme.tooltipBackgroundColor,\n borderColor: theme.tooltipBorderColor,\n textStyle: { color: theme.labelColor },\n valueFormatter: formatValue,\n },\n xAxis: isHorizontal ? valueAxisOption : categoryAxisOption,\n yAxis: isHorizontal ? categoryAxisOption : valueAxisOption,\n series: series.map((data: BarChartSeriesData, index: number) => {\n const fillColor: string =\n input.seriesColorOverrides[data.id] ??\n theme.seriesColors[index % theme.seriesColors.length];\n return {\n id: data.id,\n name: data.name,\n type: \"bar\",\n stack: stacking === \"off\" ? undefined : STACK_ID,\n barMaxWidth: BAR_MAX_WIDTH_PX,\n data: plottedValues[index],\n itemStyle: { color: fillColor },\n label: seriesLabel(input, fillColor),\n labelLayout,\n };\n }),\n };\n}\n"],"names":["GRID_PADDING","CATEGORY_LABEL_MAX_WIDTH_PX","BAR_MAX_WIDTH_PX","STACK_ID","PERCENT_SCALE","LABEL_ON_DARK_FILL","LABEL_ON_LIGHT_FILL","LABEL_ON_LIGHT_FILL_LUMINANCE","WHITE_LUMINANCE","channelLuminance","channel","normalized","parseColorChannels","color","hexMatch","digits","expanded","digit","start","rgbMatch","relativeLuminance","channels","red","green","blue","contrastRatio","lighter","darker","labelColorOnFill","fillColor","luminance","againstLightText","PERCENT_LABEL","formatPercent","value","resolveValueFormatter","input","nullSafeValueFormatter","formatValue","alignValuesToCategories","data","categories","valueByCategory","point","category","toShareOfCategoryTotal","valuesBySeries","categoryCount","categoryTotals","_unused","categoryIndex","total","values","categoryTotal","labelPosition","fitsInsideBar","params","seriesLabelLayout","hideOverlap","seriesLabel","position","categoryAxis","theme","orientation","valueAxis","options","stacking","isPercent","buildBarChartOption","series","isHorizontal","alignedValues","plottedValues","labelLayout","categoryAxisOption","valueAxisOption","index"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"bar-chart-option.js","sources":["../../../../../../src/ui/components/charts/BarChart/bar-chart-option.ts"],"sourcesContent":["import { labelColorOnFill } from \"../chart-label-color\";\nimport type { ChartTheme } from \"../chart-theme\";\n\nexport interface BarChartPoint {\n category: string;\n value: number | null;\n}\n\nexport interface BarChartSeriesData {\n id: string;\n name: string;\n points: BarChartPoint[];\n // Nome de token, nunca hex: é o que mantém a cor válida nos dois temas. Sem ele a série\n // recebe a próxima cor da paleta.\n colorToken?: string;\n}\n\nexport type BarChartOrientation = \"vertical\" | \"horizontal\";\n\nexport type BarChartStacking = \"off\" | \"stacked\" | \"percent\";\n\nexport type BarChartValueLabel = \"auto\" | \"always\" | \"never\";\n\nexport type BarChartLegendMode = \"inline\" | \"none\";\n\nexport type BarChartAxisScale = \"linear\" | \"log\";\n\nexport interface BarChartValueAxisOptions {\n scale?: BarChartAxisScale;\n min?: number;\n max?: number;\n includeZero?: boolean;\n}\n\nexport interface BarChartOptionInput {\n categories: string[];\n series: BarChartSeriesData[];\n orientation: BarChartOrientation;\n stacking: BarChartStacking;\n valueLabel: BarChartValueLabel;\n valueAxis: BarChartValueAxisOptions;\n theme: ChartTheme;\n seriesColorOverrides: Record<string, string>;\n valueFormatter?: (value: number) => string;\n}\n\n// A legenda saiu do canvas e virou HTML (`ChartLegend`), entao a grade nao reserva mais faixa\n// para ela: quem divide a altura agora e o flex do componente.\nconst GRID_PADDING = { top: 16, right: 16, bottom: 16, left: 16 } as const;\n\n// Na horizontal o rótulo da categoria disputa largura com a própria barra; na vertical ele fica\n// sob a barra e uma faixa mais estreita já dá conta.\nconst CATEGORY_LABEL_MAX_WIDTH_PX: Record<BarChartOrientation, number> = {\n vertical: 140,\n horizontal: 200,\n};\n\nconst BAR_MAX_WIDTH_PX: number = 48;\n\nconst STACK_ID: string = \"total\";\n\nconst PERCENT_SCALE: number = 100;\n\nconst PERCENT_LABEL: Intl.NumberFormat = new Intl.NumberFormat(undefined, {\n style: \"percent\",\n maximumFractionDigits: 1,\n});\n\nfunction formatPercent(value: number): string {\n return PERCENT_LABEL.format(value / PERCENT_SCALE);\n}\n\n// Em `percent` o valor desenhado é a participação na categoria, e não mais o dado do consumidor:\n// aplicar ali o formatador dele escreveria \"31 ms\" numa barra que vale 31%.\nfunction resolveValueFormatter(input: BarChartOptionInput): ((value: number) => string) | undefined {\n return input.stacking === \"percent\" ? formatPercent : input.valueFormatter;\n}\n\n// Categoria sem valor chega ao formatter como `null` — o ECharts formata o vazio junto com o\n// resto. O tipo público promete `number` ao consumidor, e é esta guarda que mantém a promessa.\nfunction nullSafeValueFormatter(\n input: BarChartOptionInput,\n): ((value: number | null) => string) | undefined {\n const formatValue: ((value: number) => string) | undefined = resolveValueFormatter(input);\n if (!formatValue) return undefined;\n return (value: number | null) => (value === null ? \"\" : formatValue(value));\n}\n\nfunction alignValuesToCategories(\n data: BarChartSeriesData,\n categories: string[],\n): Array<number | null> {\n const valueByCategory: Map<string, number | null> = new Map(\n data.points.map((point: BarChartPoint) => [point.category, point.value]),\n );\n return categories.map((category: string) => valueByCategory.get(category) ?? null);\n}\n\nfunction toShareOfCategoryTotal(\n valuesBySeries: Array<Array<number | null>>,\n categoryCount: number,\n): Array<Array<number | null>> {\n const categoryTotals: number[] = Array.from(\n { length: categoryCount },\n (_unused, categoryIndex: number) =>\n valuesBySeries.reduce(\n (total: number, values: Array<number | null>) => total + (values[categoryIndex] ?? 0),\n 0,\n ),\n );\n return valuesBySeries.map((values: Array<number | null>) =>\n values.map((value: number | null, categoryIndex: number) => {\n const categoryTotal: number = categoryTotals[categoryIndex];\n if (value === null || categoryTotal === 0) return null;\n return (value / categoryTotal) * PERCENT_SCALE;\n }),\n );\n}\n\nfunction labelPosition(input: BarChartOptionInput): string {\n if (input.stacking !== \"off\") return \"inside\";\n return input.orientation === \"horizontal\" ? \"right\" : \"top\";\n}\n\ninterface LabelLayoutParams {\n rect: { width: number; height: number };\n labelRect: { width: number; height: number };\n}\n\ninterface LabelLayout {\n hideOverlap: boolean;\n fontSize?: number;\n}\n\ntype SeriesLabelLayout = LabelLayout | ((params: LabelLayoutParams) => LabelLayout);\n\nfunction fitsInsideBar(params: LabelLayoutParams): boolean {\n return (\n params.labelRect.width <= params.rect.width && params.labelRect.height <= params.rect.height\n );\n}\n\n// `hideOverlap` resolve rótulo que encosta em rótulo, mas não rótulo maior que a própria fatia:\n// numa barra empilhada o ECharts desenha o número por cima das fatias vizinhas. `fontSize: 0` é\n// o que o `labelLayout` oferece para sumir com um rótulo caso a caso.\nfunction seriesLabelLayout(input: BarChartOptionInput): SeriesLabelLayout {\n const hideOverlap: boolean = input.valueLabel === \"auto\";\n if (!hideOverlap || labelPosition(input) !== \"inside\") return { hideOverlap };\n return (params: LabelLayoutParams) => ({\n hideOverlap,\n fontSize: fitsInsideBar(params) ? undefined : 0,\n });\n}\n\nfunction seriesLabel(input: BarChartOptionInput, fillColor: string): Record<string, unknown> {\n const position: string = labelPosition(input);\n const formatValue: ((value: number | null) => string) | undefined = nullSafeValueFormatter(input);\n return {\n show: input.valueLabel !== \"never\",\n position,\n color: position === \"inside\" ? labelColorOnFill(fillColor) : input.theme.labelColor,\n formatter: formatValue\n ? (params: { value: number | null }) => formatValue(params.value)\n : undefined,\n };\n}\n\nfunction categoryAxis(input: BarChartOptionInput): Record<string, unknown> {\n const { categories, theme, orientation } = input;\n return {\n type: \"category\",\n data: categories,\n // Na horizontal o ECharts empilha a categoria de baixo para cima, invertendo a ordem que o\n // app escolheu ao ordenar as barras.\n inverse: orientation === \"horizontal\",\n axisLine: { lineStyle: { color: theme.axisLineColor } },\n axisTick: { show: false },\n axisLabel: {\n color: theme.labelColor,\n hideOverlap: true,\n width: CATEGORY_LABEL_MAX_WIDTH_PX[orientation],\n overflow: \"truncate\",\n },\n splitLine: { show: false },\n };\n}\n\nfunction valueAxis(input: BarChartOptionInput): Record<string, unknown> {\n const { valueAxis: options, stacking, theme } = input;\n const isPercent: boolean = stacking === \"percent\";\n const formatValue: ((value: number | null) => string) | undefined = nullSafeValueFormatter(input);\n return {\n type: options.scale === \"log\" ? \"log\" : \"value\",\n // Barra mede por comprimento: eixo cortado mente sobre a proporção entre as barras, então\n // aqui o zero entra por padrão — ao contrário da série temporal, que só mostra variação.\n scale: !isPercent && options.includeZero === false,\n min: isPercent ? 0 : options.min,\n max: isPercent ? PERCENT_SCALE : options.max,\n axisLine: { show: false },\n axisLabel: { color: theme.labelColor, formatter: formatValue },\n splitLine: { lineStyle: { color: theme.splitLineColor } },\n };\n}\n\n// O retorno é `Record<string, unknown>` de propósito, e não `EChartsOption`: é o que impede\n// um tipo da biblioteca de vazar para o resto do design system.\nexport function buildBarChartOption(input: BarChartOptionInput): Record<string, unknown> {\n const { categories, series, orientation, stacking, theme } = input;\n const isHorizontal: boolean = orientation === \"horizontal\";\n const formatValue: ((value: number | null) => string) | undefined = nullSafeValueFormatter(input);\n\n const alignedValues: Array<Array<number | null>> = series.map((data: BarChartSeriesData) =>\n alignValuesToCategories(data, categories),\n );\n // O ECharts não tem empilhamento 100%: a participação precisa chegar calculada no dado.\n const plottedValues: Array<Array<number | null>> =\n stacking === \"percent\"\n ? toShareOfCategoryTotal(alignedValues, categories.length)\n : alignedValues;\n\n const labelLayout: SeriesLabelLayout = seriesLabelLayout(input);\n const categoryAxisOption: Record<string, unknown> = categoryAxis(input);\n const valueAxisOption: Record<string, unknown> = valueAxis(input);\n\n return {\n animation: false,\n color: theme.seriesColors,\n textStyle: { fontFamily: theme.fontFamily, color: theme.labelColor },\n grid: {\n ...GRID_PADDING,\n containLabel: true,\n },\n tooltip: {\n trigger: \"axis\",\n axisPointer: { type: \"shadow\" },\n backgroundColor: theme.tooltipBackgroundColor,\n borderColor: theme.tooltipBorderColor,\n textStyle: { color: theme.labelColor },\n valueFormatter: formatValue,\n },\n xAxis: isHorizontal ? valueAxisOption : categoryAxisOption,\n yAxis: isHorizontal ? categoryAxisOption : valueAxisOption,\n series: series.map((data: BarChartSeriesData, index: number) => {\n const fillColor: string =\n input.seriesColorOverrides[data.id] ??\n theme.seriesColors[index % theme.seriesColors.length];\n return {\n id: data.id,\n name: data.name,\n type: \"bar\",\n stack: stacking === \"off\" ? undefined : STACK_ID,\n barMaxWidth: BAR_MAX_WIDTH_PX,\n data: plottedValues[index],\n itemStyle: { color: fillColor },\n label: seriesLabel(input, fillColor),\n labelLayout,\n };\n }),\n };\n}\n"],"names":["GRID_PADDING","CATEGORY_LABEL_MAX_WIDTH_PX","BAR_MAX_WIDTH_PX","STACK_ID","PERCENT_SCALE","PERCENT_LABEL","formatPercent","value","resolveValueFormatter","input","nullSafeValueFormatter","formatValue","alignValuesToCategories","data","categories","valueByCategory","point","category","toShareOfCategoryTotal","valuesBySeries","categoryCount","categoryTotals","_unused","categoryIndex","total","values","categoryTotal","labelPosition","fitsInsideBar","params","seriesLabelLayout","hideOverlap","seriesLabel","fillColor","position","labelColorOnFill","categoryAxis","theme","orientation","valueAxis","options","stacking","isPercent","buildBarChartOption","series","isHorizontal","alignedValues","plottedValues","labelLayout","categoryAxisOption","valueAxisOption","index"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const o=require("react/jsx-runtime"),s=require("../../../../vendor/clsx/dist/clsx.cjs"),C=require("../ChartColorPicker/ChartColorPicker.cjs");;/* empty css */const t={toggleSeries:c=>`Show or hide ${c}`,chooseColor:c=>`Choose the color of ${c}`},u={};function x({items:c,onToggle:d,onColorChange:n,colorTokensBySeriesId:l=u,labels:r,className:i,...g}){const h=(r==null?void 0:r.toggleSeries)??t.toggleSeries,a=(r==null?void 0:r.chooseColor)??t.chooseColor;return o.jsx("div",{className:s.clsx("chart-legend",i),...g,children:c.map(e=>o.jsxs("span",{className:"chart-legend__item",children:[n?o.jsx(C.ChartColorPicker,{colorToken:l[e.id],onChange:_=>n(e.id,_),labels:{...r,palette:a(e.name)},triggerLabel:a(e.name),triggerClassName:"chart-legend__swatch chart-legend__swatch--interactive",triggerStyle:{backgroundColor:e.color}}):o.jsx("span",{className:"chart-legend__swatch",style:{backgroundColor:e.color}}),o.jsx("button",{type:"button",className:s.clsx("chart-legend__name",e.isHidden&&"chart-legend__name--hidden"),"aria-label":h(e.name),"aria-pressed":!e.isHidden,onClick:()=>d(e.id),children:e.name}),e.detail&&o.jsx("span",{className:"chart-legend__detail",children:e.detail})]},e.id))})}exports.ChartLegend=x;
|
|
2
2
|
//# sourceMappingURL=ChartLegend.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChartLegend.cjs","sources":["../../../../../../src/ui/components/charts/ChartLegend/ChartLegend.tsx"],"sourcesContent":["import clsx from \"clsx\";\nimport type { ComponentPropsWithoutRef } from \"react\";\nimport {\n ChartColorPicker,\n type ChartColorPickerLabels,\n} from \"../ChartColorPicker/ChartColorPicker\";\nimport \"./chart-legend.css\";\n\nexport interface ChartLegendItem {\n id: string;\n name: string;\n // Já resolvida, e não o token: é o mesmo valor que o canvas desenha, e é o que o seletor de cor\n // consegue comparar para marcar qual das amostras está escolhida. O tema volta a ser lido quando\n // `prefers-color-scheme` muda, e a legenda se repinta junto com o gráfico.\n color: string;\n isHidden: boolean;\n}\n\nexport interface ChartLegendLabels extends ChartColorPickerLabels {\n toggleSeries: (seriesName: string) => string;\n chooseColor: (seriesName: string) => string;\n}\n\nconst DEFAULT_LABELS: Pick<ChartLegendLabels, \"toggleSeries\" | \"chooseColor\"> = {\n toggleSeries: (seriesName: string) => `Show or hide ${seriesName}`,\n chooseColor: (seriesName: string) => `Choose the color of ${seriesName}`,\n};\n\nexport type ChartLegendProps = Omit<ComponentPropsWithoutRef<\"div\">, \"children\" | \"onToggle\"> & {\n items: ChartLegendItem[];\n onToggle: (seriesId: string) => void;\n // A presença é o que habilita o seletor no marcador de cor: sem ela a amostra é desenho, e não\n // controle — o gráfico que não guarda cor escolhida não pode oferecer a escolha.\n onColorChange?: (seriesId: string, colorToken: string | null) => void;\n // O token por trás da cor de cada série, por id: é o que o seletor de cor recebe.\n colorTokensBySeriesId?: Record<string, string>;\n labels?: Partial<ChartLegendLabels>;\n};\n\nconst NO_COLOR_TOKENS: Record<string, string> = {};\n\nexport function ChartLegend({\n items,\n onToggle,\n onColorChange,\n colorTokensBySeriesId = NO_COLOR_TOKENS,\n labels,\n className,\n ...props\n}: ChartLegendProps) {\n const toggleSeriesLabel = labels?.toggleSeries ?? DEFAULT_LABELS.toggleSeries;\n const chooseColorLabel = labels?.chooseColor ?? DEFAULT_LABELS.chooseColor;\n\n return (\n <div className={clsx(\"chart-legend\", className)} {...props}>\n {items.map((item: ChartLegendItem) => (\n <span key={item.id} className=\"chart-legend__item\">\n {/* Dois controles por entrada, como no Grafana: a amostra escolhe a cor e o nome mostra\n ou esconde a série. Um controle só teria que decidir qual dos dois gestos perder. */}\n {onColorChange ? (\n <ChartColorPicker\n colorToken={colorTokensBySeriesId[item.id]}\n onChange={(colorToken: string | null) => onColorChange(item.id, colorToken)}\n labels={{ ...labels, palette: chooseColorLabel(item.name) }}\n triggerLabel={chooseColorLabel(item.name)}\n triggerClassName=\"chart-legend__swatch chart-legend__swatch--interactive\"\n triggerStyle={{ backgroundColor: item.color }}\n />\n ) : (\n <span className=\"chart-legend__swatch\" style={{ backgroundColor: item.color }} />\n )}\n\n <button\n type=\"button\"\n className={clsx(\"chart-legend__name\", item.isHidden && \"chart-legend__name--hidden\")}\n aria-label={toggleSeriesLabel(item.name)}\n aria-pressed={!item.isHidden}\n onClick={() => onToggle(item.id)}\n >\n {item.name}\n </button>\n </span>\n ))}\n </div>\n );\n}\n"],"names":["DEFAULT_LABELS","seriesName","NO_COLOR_TOKENS","ChartLegend","items","onToggle","onColorChange","colorTokensBySeriesId","labels","className","props","toggleSeriesLabel","chooseColorLabel","clsx","item","jsxs","jsx","ChartColorPicker","colorToken"],"mappings":"
|
|
1
|
+
{"version":3,"file":"ChartLegend.cjs","sources":["../../../../../../src/ui/components/charts/ChartLegend/ChartLegend.tsx"],"sourcesContent":["import clsx from \"clsx\";\nimport type { ComponentPropsWithoutRef } from \"react\";\nimport {\n ChartColorPicker,\n type ChartColorPickerLabels,\n} from \"../ChartColorPicker/ChartColorPicker\";\nimport \"./chart-legend.css\";\n\nexport interface ChartLegendItem {\n id: string;\n name: string;\n // Já resolvida, e não o token: é o mesmo valor que o canvas desenha, e é o que o seletor de cor\n // consegue comparar para marcar qual das amostras está escolhida. O tema volta a ser lido quando\n // `prefers-color-scheme` muda, e a legenda se repinta junto com o gráfico.\n color: string;\n isHidden: boolean;\n detail?: string;\n}\n\nexport interface ChartLegendLabels extends ChartColorPickerLabels {\n toggleSeries: (seriesName: string) => string;\n chooseColor: (seriesName: string) => string;\n}\n\nconst DEFAULT_LABELS: Pick<ChartLegendLabels, \"toggleSeries\" | \"chooseColor\"> = {\n toggleSeries: (seriesName: string) => `Show or hide ${seriesName}`,\n chooseColor: (seriesName: string) => `Choose the color of ${seriesName}`,\n};\n\nexport type ChartLegendProps = Omit<ComponentPropsWithoutRef<\"div\">, \"children\" | \"onToggle\"> & {\n items: ChartLegendItem[];\n onToggle: (seriesId: string) => void;\n // A presença é o que habilita o seletor no marcador de cor: sem ela a amostra é desenho, e não\n // controle — o gráfico que não guarda cor escolhida não pode oferecer a escolha.\n onColorChange?: (seriesId: string, colorToken: string | null) => void;\n // O token por trás da cor de cada série, por id: é o que o seletor de cor recebe.\n colorTokensBySeriesId?: Record<string, string>;\n labels?: Partial<ChartLegendLabels>;\n};\n\nconst NO_COLOR_TOKENS: Record<string, string> = {};\n\nexport function ChartLegend({\n items,\n onToggle,\n onColorChange,\n colorTokensBySeriesId = NO_COLOR_TOKENS,\n labels,\n className,\n ...props\n}: ChartLegendProps) {\n const toggleSeriesLabel = labels?.toggleSeries ?? DEFAULT_LABELS.toggleSeries;\n const chooseColorLabel = labels?.chooseColor ?? DEFAULT_LABELS.chooseColor;\n\n return (\n <div className={clsx(\"chart-legend\", className)} {...props}>\n {items.map((item: ChartLegendItem) => (\n <span key={item.id} className=\"chart-legend__item\">\n {/* Dois controles por entrada, como no Grafana: a amostra escolhe a cor e o nome mostra\n ou esconde a série. Um controle só teria que decidir qual dos dois gestos perder. */}\n {onColorChange ? (\n <ChartColorPicker\n colorToken={colorTokensBySeriesId[item.id]}\n onChange={(colorToken: string | null) => onColorChange(item.id, colorToken)}\n labels={{ ...labels, palette: chooseColorLabel(item.name) }}\n triggerLabel={chooseColorLabel(item.name)}\n triggerClassName=\"chart-legend__swatch chart-legend__swatch--interactive\"\n triggerStyle={{ backgroundColor: item.color }}\n />\n ) : (\n <span className=\"chart-legend__swatch\" style={{ backgroundColor: item.color }} />\n )}\n\n <button\n type=\"button\"\n className={clsx(\"chart-legend__name\", item.isHidden && \"chart-legend__name--hidden\")}\n aria-label={toggleSeriesLabel(item.name)}\n aria-pressed={!item.isHidden}\n onClick={() => onToggle(item.id)}\n >\n {item.name}\n </button>\n\n {item.detail && <span className=\"chart-legend__detail\">{item.detail}</span>}\n </span>\n ))}\n </div>\n );\n}\n"],"names":["DEFAULT_LABELS","seriesName","NO_COLOR_TOKENS","ChartLegend","items","onToggle","onColorChange","colorTokensBySeriesId","labels","className","props","toggleSeriesLabel","chooseColorLabel","clsx","item","jsxs","jsx","ChartColorPicker","colorToken"],"mappings":"gQAwBA,MAAMA,EAA0E,CAC9E,aAAeC,GAAuB,gBAAgBA,CAAU,GAChE,YAAcA,GAAuB,uBAAuBA,CAAU,EACxE,EAaMC,EAA0C,CAAA,EAEzC,SAASC,EAAY,CAC1B,MAAAC,EACA,SAAAC,EACA,cAAAC,EACA,sBAAAC,EAAwBL,EACxB,OAAAM,EACA,UAAAC,EACA,GAAGC,CACL,EAAqB,CACnB,MAAMC,GAAoBH,GAAA,YAAAA,EAAQ,eAAgBR,EAAe,aAC3DY,GAAmBJ,GAAA,YAAAA,EAAQ,cAAeR,EAAe,YAE/D,aACG,MAAA,CAAI,UAAWa,EAAAA,KAAK,eAAgBJ,CAAS,EAAI,GAAGC,EAClD,SAAAN,EAAM,IAAKU,GACVC,EAAAA,KAAC,OAAA,CAAmB,UAAU,qBAG3B,SAAA,CAAAT,EACCU,EAAAA,IAACC,EAAAA,iBAAA,CACC,WAAYV,EAAsBO,EAAK,EAAE,EACzC,SAAWI,GAA8BZ,EAAcQ,EAAK,GAAII,CAAU,EAC1E,OAAQ,CAAE,GAAGV,EAAQ,QAASI,EAAiBE,EAAK,IAAI,CAAA,EACxD,aAAcF,EAAiBE,EAAK,IAAI,EACxC,iBAAiB,yDACjB,aAAc,CAAE,gBAAiBA,EAAK,KAAA,CAAM,CAAA,EAG9CE,MAAC,OAAA,CAAK,UAAU,uBAAuB,MAAO,CAAE,gBAAiBF,EAAK,KAAA,EAAS,EAGjFE,EAAAA,IAAC,SAAA,CACC,KAAK,SACL,UAAWH,EAAAA,KAAK,qBAAsBC,EAAK,UAAY,4BAA4B,EACnF,aAAYH,EAAkBG,EAAK,IAAI,EACvC,eAAc,CAACA,EAAK,SACpB,QAAS,IAAMT,EAASS,EAAK,EAAE,EAE9B,SAAAA,EAAK,IAAA,CAAA,EAGPA,EAAK,QAAUE,EAAAA,IAAC,QAAK,UAAU,uBAAwB,WAAK,MAAA,CAAO,CAAA,CAAA,EA1B3DF,EAAK,EA2BhB,CACD,EACH,CAEJ"}
|
|
@@ -1,45 +1,46 @@
|
|
|
1
|
-
import { jsx as
|
|
1
|
+
import { jsx as a, jsxs as p } from "react/jsx-runtime";
|
|
2
2
|
import { clsx as t } from "../../../../vendor/clsx/dist/clsx.js";
|
|
3
|
-
import { ChartColorPicker as
|
|
3
|
+
import { ChartColorPicker as C } from "../ChartColorPicker/ChartColorPicker.js";
|
|
4
4
|
/* empty css */
|
|
5
|
-
const
|
|
5
|
+
const d = {
|
|
6
6
|
toggleSeries: (r) => `Show or hide ${r}`,
|
|
7
7
|
chooseColor: (r) => `Choose the color of ${r}`
|
|
8
8
|
}, m = {};
|
|
9
9
|
function f({
|
|
10
10
|
items: r,
|
|
11
|
-
onToggle:
|
|
12
|
-
onColorChange:
|
|
13
|
-
colorTokensBySeriesId:
|
|
14
|
-
labels:
|
|
11
|
+
onToggle: s,
|
|
12
|
+
onColorChange: c,
|
|
13
|
+
colorTokensBySeriesId: l = m,
|
|
14
|
+
labels: o,
|
|
15
15
|
className: h,
|
|
16
|
-
...
|
|
16
|
+
...g
|
|
17
17
|
}) {
|
|
18
|
-
const
|
|
19
|
-
return /* @__PURE__ */
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
const i = (o == null ? void 0 : o.toggleSeries) ?? d.toggleSeries, n = (o == null ? void 0 : o.chooseColor) ?? d.chooseColor;
|
|
19
|
+
return /* @__PURE__ */ a("div", { className: t("chart-legend", h), ...g, children: r.map((e) => /* @__PURE__ */ p("span", { className: "chart-legend__item", children: [
|
|
20
|
+
c ? /* @__PURE__ */ a(
|
|
21
|
+
C,
|
|
22
22
|
{
|
|
23
|
-
colorToken:
|
|
24
|
-
onChange: (_) =>
|
|
25
|
-
labels: { ...
|
|
26
|
-
triggerLabel:
|
|
23
|
+
colorToken: l[e.id],
|
|
24
|
+
onChange: (_) => c(e.id, _),
|
|
25
|
+
labels: { ...o, palette: n(e.name) },
|
|
26
|
+
triggerLabel: n(e.name),
|
|
27
27
|
triggerClassName: "chart-legend__swatch chart-legend__swatch--interactive",
|
|
28
|
-
triggerStyle: { backgroundColor:
|
|
28
|
+
triggerStyle: { backgroundColor: e.color }
|
|
29
29
|
}
|
|
30
|
-
) : /* @__PURE__ */
|
|
31
|
-
/* @__PURE__ */
|
|
30
|
+
) : /* @__PURE__ */ a("span", { className: "chart-legend__swatch", style: { backgroundColor: e.color } }),
|
|
31
|
+
/* @__PURE__ */ a(
|
|
32
32
|
"button",
|
|
33
33
|
{
|
|
34
34
|
type: "button",
|
|
35
|
-
className: t("chart-legend__name",
|
|
36
|
-
"aria-label":
|
|
37
|
-
"aria-pressed": !
|
|
38
|
-
onClick: () =>
|
|
39
|
-
children:
|
|
35
|
+
className: t("chart-legend__name", e.isHidden && "chart-legend__name--hidden"),
|
|
36
|
+
"aria-label": i(e.name),
|
|
37
|
+
"aria-pressed": !e.isHidden,
|
|
38
|
+
onClick: () => s(e.id),
|
|
39
|
+
children: e.name
|
|
40
40
|
}
|
|
41
|
-
)
|
|
42
|
-
|
|
41
|
+
),
|
|
42
|
+
e.detail && /* @__PURE__ */ a("span", { className: "chart-legend__detail", children: e.detail })
|
|
43
|
+
] }, e.id)) });
|
|
43
44
|
}
|
|
44
45
|
export {
|
|
45
46
|
f as ChartLegend
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChartLegend.js","sources":["../../../../../../src/ui/components/charts/ChartLegend/ChartLegend.tsx"],"sourcesContent":["import clsx from \"clsx\";\nimport type { ComponentPropsWithoutRef } from \"react\";\nimport {\n ChartColorPicker,\n type ChartColorPickerLabels,\n} from \"../ChartColorPicker/ChartColorPicker\";\nimport \"./chart-legend.css\";\n\nexport interface ChartLegendItem {\n id: string;\n name: string;\n // Já resolvida, e não o token: é o mesmo valor que o canvas desenha, e é o que o seletor de cor\n // consegue comparar para marcar qual das amostras está escolhida. O tema volta a ser lido quando\n // `prefers-color-scheme` muda, e a legenda se repinta junto com o gráfico.\n color: string;\n isHidden: boolean;\n}\n\nexport interface ChartLegendLabels extends ChartColorPickerLabels {\n toggleSeries: (seriesName: string) => string;\n chooseColor: (seriesName: string) => string;\n}\n\nconst DEFAULT_LABELS: Pick<ChartLegendLabels, \"toggleSeries\" | \"chooseColor\"> = {\n toggleSeries: (seriesName: string) => `Show or hide ${seriesName}`,\n chooseColor: (seriesName: string) => `Choose the color of ${seriesName}`,\n};\n\nexport type ChartLegendProps = Omit<ComponentPropsWithoutRef<\"div\">, \"children\" | \"onToggle\"> & {\n items: ChartLegendItem[];\n onToggle: (seriesId: string) => void;\n // A presença é o que habilita o seletor no marcador de cor: sem ela a amostra é desenho, e não\n // controle — o gráfico que não guarda cor escolhida não pode oferecer a escolha.\n onColorChange?: (seriesId: string, colorToken: string | null) => void;\n // O token por trás da cor de cada série, por id: é o que o seletor de cor recebe.\n colorTokensBySeriesId?: Record<string, string>;\n labels?: Partial<ChartLegendLabels>;\n};\n\nconst NO_COLOR_TOKENS: Record<string, string> = {};\n\nexport function ChartLegend({\n items,\n onToggle,\n onColorChange,\n colorTokensBySeriesId = NO_COLOR_TOKENS,\n labels,\n className,\n ...props\n}: ChartLegendProps) {\n const toggleSeriesLabel = labels?.toggleSeries ?? DEFAULT_LABELS.toggleSeries;\n const chooseColorLabel = labels?.chooseColor ?? DEFAULT_LABELS.chooseColor;\n\n return (\n <div className={clsx(\"chart-legend\", className)} {...props}>\n {items.map((item: ChartLegendItem) => (\n <span key={item.id} className=\"chart-legend__item\">\n {/* Dois controles por entrada, como no Grafana: a amostra escolhe a cor e o nome mostra\n ou esconde a série. Um controle só teria que decidir qual dos dois gestos perder. */}\n {onColorChange ? (\n <ChartColorPicker\n colorToken={colorTokensBySeriesId[item.id]}\n onChange={(colorToken: string | null) => onColorChange(item.id, colorToken)}\n labels={{ ...labels, palette: chooseColorLabel(item.name) }}\n triggerLabel={chooseColorLabel(item.name)}\n triggerClassName=\"chart-legend__swatch chart-legend__swatch--interactive\"\n triggerStyle={{ backgroundColor: item.color }}\n />\n ) : (\n <span className=\"chart-legend__swatch\" style={{ backgroundColor: item.color }} />\n )}\n\n <button\n type=\"button\"\n className={clsx(\"chart-legend__name\", item.isHidden && \"chart-legend__name--hidden\")}\n aria-label={toggleSeriesLabel(item.name)}\n aria-pressed={!item.isHidden}\n onClick={() => onToggle(item.id)}\n >\n {item.name}\n </button>\n </span>\n ))}\n </div>\n );\n}\n"],"names":["DEFAULT_LABELS","seriesName","NO_COLOR_TOKENS","ChartLegend","items","onToggle","onColorChange","colorTokensBySeriesId","labels","className","props","toggleSeriesLabel","chooseColorLabel","clsx","item","jsxs","jsx","ChartColorPicker","colorToken"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"ChartLegend.js","sources":["../../../../../../src/ui/components/charts/ChartLegend/ChartLegend.tsx"],"sourcesContent":["import clsx from \"clsx\";\nimport type { ComponentPropsWithoutRef } from \"react\";\nimport {\n ChartColorPicker,\n type ChartColorPickerLabels,\n} from \"../ChartColorPicker/ChartColorPicker\";\nimport \"./chart-legend.css\";\n\nexport interface ChartLegendItem {\n id: string;\n name: string;\n // Já resolvida, e não o token: é o mesmo valor que o canvas desenha, e é o que o seletor de cor\n // consegue comparar para marcar qual das amostras está escolhida. O tema volta a ser lido quando\n // `prefers-color-scheme` muda, e a legenda se repinta junto com o gráfico.\n color: string;\n isHidden: boolean;\n detail?: string;\n}\n\nexport interface ChartLegendLabels extends ChartColorPickerLabels {\n toggleSeries: (seriesName: string) => string;\n chooseColor: (seriesName: string) => string;\n}\n\nconst DEFAULT_LABELS: Pick<ChartLegendLabels, \"toggleSeries\" | \"chooseColor\"> = {\n toggleSeries: (seriesName: string) => `Show or hide ${seriesName}`,\n chooseColor: (seriesName: string) => `Choose the color of ${seriesName}`,\n};\n\nexport type ChartLegendProps = Omit<ComponentPropsWithoutRef<\"div\">, \"children\" | \"onToggle\"> & {\n items: ChartLegendItem[];\n onToggle: (seriesId: string) => void;\n // A presença é o que habilita o seletor no marcador de cor: sem ela a amostra é desenho, e não\n // controle — o gráfico que não guarda cor escolhida não pode oferecer a escolha.\n onColorChange?: (seriesId: string, colorToken: string | null) => void;\n // O token por trás da cor de cada série, por id: é o que o seletor de cor recebe.\n colorTokensBySeriesId?: Record<string, string>;\n labels?: Partial<ChartLegendLabels>;\n};\n\nconst NO_COLOR_TOKENS: Record<string, string> = {};\n\nexport function ChartLegend({\n items,\n onToggle,\n onColorChange,\n colorTokensBySeriesId = NO_COLOR_TOKENS,\n labels,\n className,\n ...props\n}: ChartLegendProps) {\n const toggleSeriesLabel = labels?.toggleSeries ?? DEFAULT_LABELS.toggleSeries;\n const chooseColorLabel = labels?.chooseColor ?? DEFAULT_LABELS.chooseColor;\n\n return (\n <div className={clsx(\"chart-legend\", className)} {...props}>\n {items.map((item: ChartLegendItem) => (\n <span key={item.id} className=\"chart-legend__item\">\n {/* Dois controles por entrada, como no Grafana: a amostra escolhe a cor e o nome mostra\n ou esconde a série. Um controle só teria que decidir qual dos dois gestos perder. */}\n {onColorChange ? (\n <ChartColorPicker\n colorToken={colorTokensBySeriesId[item.id]}\n onChange={(colorToken: string | null) => onColorChange(item.id, colorToken)}\n labels={{ ...labels, palette: chooseColorLabel(item.name) }}\n triggerLabel={chooseColorLabel(item.name)}\n triggerClassName=\"chart-legend__swatch chart-legend__swatch--interactive\"\n triggerStyle={{ backgroundColor: item.color }}\n />\n ) : (\n <span className=\"chart-legend__swatch\" style={{ backgroundColor: item.color }} />\n )}\n\n <button\n type=\"button\"\n className={clsx(\"chart-legend__name\", item.isHidden && \"chart-legend__name--hidden\")}\n aria-label={toggleSeriesLabel(item.name)}\n aria-pressed={!item.isHidden}\n onClick={() => onToggle(item.id)}\n >\n {item.name}\n </button>\n\n {item.detail && <span className=\"chart-legend__detail\">{item.detail}</span>}\n </span>\n ))}\n </div>\n );\n}\n"],"names":["DEFAULT_LABELS","seriesName","NO_COLOR_TOKENS","ChartLegend","items","onToggle","onColorChange","colorTokensBySeriesId","labels","className","props","toggleSeriesLabel","chooseColorLabel","clsx","item","jsxs","jsx","ChartColorPicker","colorToken"],"mappings":";;;;AAwBA,MAAMA,IAA0E;AAAA,EAC9E,cAAc,CAACC,MAAuB,gBAAgBA,CAAU;AAAA,EAChE,aAAa,CAACA,MAAuB,uBAAuBA,CAAU;AACxE,GAaMC,IAA0C,CAAA;AAEzC,SAASC,EAAY;AAAA,EAC1B,OAAAC;AAAA,EACA,UAAAC;AAAA,EACA,eAAAC;AAAA,EACA,uBAAAC,IAAwBL;AAAA,EACxB,QAAAM;AAAA,EACA,WAAAC;AAAA,EACA,GAAGC;AACL,GAAqB;AACnB,QAAMC,KAAoBH,KAAA,gBAAAA,EAAQ,iBAAgBR,EAAe,cAC3DY,KAAmBJ,KAAA,gBAAAA,EAAQ,gBAAeR,EAAe;AAE/D,2BACG,OAAA,EAAI,WAAWa,EAAK,gBAAgBJ,CAAS,GAAI,GAAGC,GAClD,UAAAN,EAAM,IAAI,CAACU,MACV,gBAAAC,EAAC,QAAA,EAAmB,WAAU,sBAG3B,UAAA;AAAA,IAAAT,IACC,gBAAAU;AAAA,MAACC;AAAA,MAAA;AAAA,QACC,YAAYV,EAAsBO,EAAK,EAAE;AAAA,QACzC,UAAU,CAACI,MAA8BZ,EAAcQ,EAAK,IAAII,CAAU;AAAA,QAC1E,QAAQ,EAAE,GAAGV,GAAQ,SAASI,EAAiBE,EAAK,IAAI,EAAA;AAAA,QACxD,cAAcF,EAAiBE,EAAK,IAAI;AAAA,QACxC,kBAAiB;AAAA,QACjB,cAAc,EAAE,iBAAiBA,EAAK,MAAA;AAAA,MAAM;AAAA,IAAA,IAG9C,gBAAAE,EAAC,QAAA,EAAK,WAAU,wBAAuB,OAAO,EAAE,iBAAiBF,EAAK,MAAA,GAAS;AAAA,IAGjF,gBAAAE;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,MAAK;AAAA,QACL,WAAWH,EAAK,sBAAsBC,EAAK,YAAY,4BAA4B;AAAA,QACnF,cAAYH,EAAkBG,EAAK,IAAI;AAAA,QACvC,gBAAc,CAACA,EAAK;AAAA,QACpB,SAAS,MAAMT,EAASS,EAAK,EAAE;AAAA,QAE9B,UAAAA,EAAK;AAAA,MAAA;AAAA,IAAA;AAAA,IAGPA,EAAK,UAAU,gBAAAE,EAAC,UAAK,WAAU,wBAAwB,YAAK,OAAA,CAAO;AAAA,EAAA,EAAA,GA1B3DF,EAAK,EA2BhB,CACD,GACH;AAEJ;"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const n=require("react/jsx-runtime"),ee=require("../../../../vendor/clsx/dist/clsx.cjs"),i=require("react"),te=require("../ChartLegend/ChartLegend.cjs"),re=require("../ChartLegend/use-chart-legend.cjs"),ne=require("../chart-palette.cjs"),ie=require("../useChartInstance.cjs"),L=require("./pie-chart-option.cjs"),f=require("./pie-chart-slices.cjs");;/* empty css */const se={others:"Others",total:"Total",backToPrevious:e=>`Back from ${e}`},ce=200,oe=["percent"],ae=[];function le(e,s,a){if(!e||s.length===0)return;const l=s.includes("percent"),S=s.includes("value");return l&&S?`${a(e.value)} (${L.formatShare(e.share)})`:S?a(e.value):L.formatShare(e.share)}function ue(e){const[s,a]=i.useState(0);return i.useLayoutEffect(()=>{if(!e)return;const l=new ResizeObserver(()=>a(Math.min(e.clientWidth,e.clientHeight)));return l.observe(e),()=>l.disconnect()},[e]),s}function de({data:e,shape:s="donut",labelContent:a=oe,showTotal:l="auto",total:S,valueFormatter:p,legend:y="inline",legendPosition:q="bottom",legendValues:M=ae,onSeriesColorChange:A,onFocusChange:b,legendLabels:R,labels:B,className:$,...D}){const{containerRef:U,chart:u,theme:c,resolveSeriesColor:H}=ie.useChartInstance(),o={...se,...B},[h,V]=i.useState(null),[w,z]=i.useState(null),G=ue(w),K=i.useMemo(()=>e.map(t=>({id:t.id,name:f.displayNameOf(t,o.others),colorToken:t.colorToken??(t.isOthers?ne.CHART_NEUTRAL_COLOR_TOKEN:void 0)})),[e,o.others]),{items:W,colorsBySeriesId:O,colorTokensBySeriesId:X,hiddenSeriesIds:k,toggleSeries:Z}=re.useChartLegend(K,(c==null?void 0:c.seriesColors)??[],H),d=i.useMemo(()=>f.buildPieSlices({nodes:e.filter(t=>!k.has(t.id)),colorsByRootId:O,fallbackColor:(c==null?void 0:c.neutralColor)??"#9ca3af",othersName:o.others}),[e,k,O,c,o.others]),r=i.useMemo(()=>h===null?void 0:f.findSlice(d,h),[d,h]),m=r?r.children:d,_=i.useCallback(t=>{V(t),b==null||b(t)},[b]);i.useEffect(()=>{h!==null&&!r&&_(null)},[h,r,_]),i.useEffect(()=>{!u||!c||u.setOption(L.buildPieChartOption({slices:m,shape:s,labelContent:a,theme:c,valueFormatter:p}),{notMerge:!0})},[u,c,m,s,a,p]),i.useEffect(()=>{if(!u)return;function t(C){var I;const P=(I=C.data)==null?void 0:I.id;if(typeof P!="string")return;const T=f.findSlice(m,P);T&&T.children.length>0&&_(T.id)}return u.on("click",t),()=>{u.off("click",t)}},[u,m,_]);function v(t){return p?p(t):String(t)}const J=W.map(t=>({...t,detail:le(d.find(C=>C.id===t.id),M,v)})),g=l==="on"||l==="auto"&&G>=ce,N=r?r.value:S??f.totalOf(d),E=r?f.findParentSlice(d,r.id):void 0,Q=y!=="none"&&e.length>0,j=n.jsxs(n.Fragment,{children:[n.jsx("span",{className:"pie-chart__total-value",children:v(N)}),n.jsx("span",{className:"pie-chart__total-caption",children:r?r.name:o.total})]});function x(){_(E?E.id:null)}function Y(){return r?n.jsx("button",{type:"button",className:"pie-chart__center pie-chart__center--button","aria-label":o.backToPrevious(r.name),onClick:x,children:g&&j}):g&&n.jsx("div",{className:"pie-chart__center",children:j})}function F(){return r?n.jsxs("button",{type:"button",className:"pie-chart__footer pie-chart__footer--button","aria-label":o.backToPrevious(r.name),onClick:x,children:[r.name,g&&`: ${v(N)}`]}):g&&n.jsx("div",{className:"pie-chart__footer",children:`${o.total}: ${v(N)}`})}return n.jsxs("div",{className:ee.clsx("pie-chart",`pie-chart--legend-${q}`,$),...D,children:[Q&&n.jsx(te.ChartLegend,{className:"pie-chart__legend",items:J,onToggle:Z,onColorChange:A,colorTokensBySeriesId:X,labels:R}),n.jsxs("div",{className:"pie-chart__body",children:[n.jsxs("div",{ref:z,className:"pie-chart__stage",children:[n.jsx("div",{ref:U,className:"pie-chart__canvas"}),s==="donut"&&Y()]}),s==="pie"&&F()]})]})}exports.PieChart=de;
|
|
2
|
+
//# sourceMappingURL=PieChart.cjs.map
|