feat: initial Pezkuwi Apps rebrand from polkadot-apps

Rebranded terminology:
- Polkadot → Pezkuwi
- Kusama → Dicle
- Westend → Zagros
- Rococo → PezkuwiChain
- Substrate → Bizinikiwi
- parachain → teyrchain

Custom logos with Kurdistan brand colors (#e6007a → #86e62a):
- bizinikiwi-hexagon.svg
- sora-bizinikiwi.svg
- hezscanner.svg
- heztreasury.svg
- pezkuwiscan.svg
- pezkuwistats.svg
- pezkuwiassembly.svg
- pezkuwiholic.svg
This commit is contained in:
2026-01-07 13:05:27 +03:00
commit d21bfb1320
5867 changed files with 329019 additions and 0 deletions
@@ -0,0 +1,29 @@
// Copyright 2017-2025 @pezkuwi/react-components authors & contributors
// SPDX-License-Identifier: Apache-2.0
import React from 'react';
import { styled } from '../styled.js';
interface Props {
children: React.ReactNode;
className?: string;
}
function BaseChart ({ children, className = '' }: Props): React.ReactElement<Props> {
return (
<StyledDiv className={`${className} ui--Chart`}>
{children}
</StyledDiv>
);
}
const StyledDiv = styled.div`
position: relative;
display: inline-block;
padding: 1em 1em 0;
height: 15vw;
width: 15vw;
`;
export default React.memo(BaseChart);
@@ -0,0 +1,65 @@
// Copyright 2017-2025 @pezkuwi/react-components authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { BN } from '@pezkuwi/util';
import React from 'react';
import { Doughnut } from 'react-chartjs-2';
import { bnToBn } from '@pezkuwi/util';
import Base from './Base.js';
interface DoughnutValue {
colors: string[];
label: string;
value: number | BN;
}
export interface Props {
className?: string;
size?: number;
values: DoughnutValue[];
}
interface Options {
colorNormal: string[];
colorHover: string[];
data: number[];
labels: string[];
}
function ChartDoughnut ({ className = '', size = 100, values }: Props): React.ReactElement<Props> {
const options: Options = {
colorHover: [],
colorNormal: [],
data: [],
labels: []
};
values.forEach(({ colors: [normalColor = '#00f', hoverColor], label, value }): void => {
options.colorNormal.push(normalColor);
options.colorHover.push(hoverColor || normalColor);
options.data.push(bnToBn(value).toNumber());
options.labels.push(label);
});
return (
<Base className={`${className} ui--Chart-Doughnut`}>
<Doughnut
data={{
datasets: [{
backgroundColor: options.colorNormal,
data: options.data,
hoverBackgroundColor: options.colorHover
}],
labels: options.labels
}}
height={size}
width={size}
/>
</Base>
);
}
export default React.memo(ChartDoughnut);
@@ -0,0 +1,119 @@
// Copyright 2017-2025 @pezkuwi/react-components authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { ChartData, ChartOptions, TooltipItem } from 'chart.js';
import type { BN } from '@pezkuwi/util';
import React, { useEffect, useState } from 'react';
import { Bar } from 'react-chartjs-2';
import { bnToBn, isNumber } from '@pezkuwi/util';
import { alphaColor } from './utils.js';
interface Value {
colors: string[];
label: string;
tooltip?: string;
value: number | BN;
}
export interface Props {
aspectRatio?: number;
className?: string;
max?: number;
showLabels?: boolean;
values: Value[];
withColors?: boolean;
}
interface State {
chartData?: ChartData;
chartOptions?: ChartOptions;
jsonValues?: string;
}
interface Config {
labels: string[];
datasets: {
data: number[];
backgroundColor: string[];
hoverBackgroundColor: string[];
}[];
}
function calculateOptions (aspectRatio: number, values: Value[], jsonValues: string, max: number, showLabels: boolean): State {
const chartData = values.reduce((data, { colors: [normalColor = '#00f', hoverColor], label, value }): Config => {
const dataset = data.datasets[0];
dataset.backgroundColor.push(alphaColor(normalColor));
dataset.hoverBackgroundColor.push(alphaColor(hoverColor || normalColor));
dataset.data.push(isNumber(value) ? value : bnToBn(value).toNumber());
data.labels.push(label);
return data;
}, {
datasets: [{
backgroundColor: [] as string[],
data: [] as number[],
hoverBackgroundColor: [] as string[]
}],
labels: [] as string[]
});
return {
chartData,
chartOptions: {
aspectRatio,
plugins: {
legend: {
display: false
},
tooltip: {
callbacks: {
label: (item: TooltipItem<any>): string =>
values[item.dataIndex].tooltip || values[item.dataIndex].label
}
}
},
scales: {
x: showLabels
? { beginAtZero: true, max }
: { display: false }
}
},
jsonValues
};
}
function ChartHorizBar ({ aspectRatio = 8, className = '', max = 100, showLabels = false, values }: Props): React.ReactElement<Props> | null {
const [{ chartData, chartOptions, jsonValues }, setState] = useState<State>({});
useEffect((): void => {
const newJsonValues = JSON.stringify(values);
if (newJsonValues !== jsonValues) {
setState(calculateOptions(aspectRatio, values, newJsonValues, max, showLabels));
}
}, [aspectRatio, jsonValues, max, showLabels, values]);
if (!chartData) {
return null;
}
// HACK on width/height to get the aspectRatio to work
return (
<div className={`${className} ui--Chart-HorizBar`}>
<Bar
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
data={chartData as any}
height={null as unknown as number}
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
options={chartOptions as any}
width={null as unknown as number}
/>
</div>
);
}
export default React.memo(ChartHorizBar);
@@ -0,0 +1,156 @@
// Copyright 2017-2025 @pezkuwi/react-components authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { ChartData, ChartDataset, ChartOptions, DatasetChartOptions } from 'chart.js';
import type { BN } from '@pezkuwi/util';
import React, { useMemo } from 'react';
import * as Chart from 'react-chartjs-2';
import { isBn, objectSpread } from '@pezkuwi/util';
import ErrorBoundary from '../ErrorBoundary.js';
import { styled } from '../styled.js';
import { alphaColor } from './utils.js';
export interface Props {
colors?: (string | undefined)[];
className?: string;
labels: string[];
legends: string[];
options?: ChartOptions;
values: (number | BN)[][];
title?: React.ReactNode;
}
interface Config {
labels: string[];
datasets: ChartDataset<'line'>[];
}
const COLORS = ['#ff8c00', '#008c8c', '#8c008c'];
const BASE_OPTS: ChartOptions = {
animation: {
duration: 0
},
elements: {
point: {
hoverRadius: 6,
radius: 0
}
},
hover: {
intersect: false
},
interaction: {
intersect: false,
mode: 'index'
},
plugins: {
crosshair: {
line: {
color: '#ff8c00',
dashPattern: [5, 5],
width: 2
},
snap: {
enabled: true
},
sync: {
enabled: true
},
// this would be nice, but atm just doesn't quite
// seem or feel intuitive...
zoom: {
enabled: false
}
},
legend: {
display: false
},
tooltip: {
intersect: false
}
},
scales: {
x: {
ticks: {
maxRotation: 60,
minRotation: 60
}
}
}
};
function getOptions (options: ChartOptions = {}): DatasetChartOptions<'line'> {
return objectSpread({}, BASE_OPTS, options, {
// Re-spread plugins for deep(er) copy
plugins: objectSpread({}, BASE_OPTS.plugins, options.plugins, {
// Same applied to plugins, we may want specific values
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
annotation: objectSpread({}, BASE_OPTS.plugins?.annotation, options.plugins?.annotation),
crosshair: objectSpread({}, BASE_OPTS.plugins?.crosshair, options.plugins?.crosshair),
tooltip: objectSpread({}, BASE_OPTS.plugins?.tooltip, options.plugins?.tooltip)
}),
scales: objectSpread({}, BASE_OPTS.scales, options.scales, {
x: objectSpread({}, BASE_OPTS.scales?.x, options.scales?.x),
y: objectSpread({}, BASE_OPTS.scales?.y, options.scales?.y)
})
});
}
function getData (colors: (string | undefined)[] = [], legends: string[], labels: string[], values: (number | BN)[][]): ChartData<'line'> {
return values.reduce((chartData, values, index): Config => {
const color = colors[index] || alphaColor(COLORS[index]);
const data = values.map((value): number => isBn(value) ? value.toNumber() : value);
chartData.datasets.push({
backgroundColor: color,
borderColor: color,
cubicInterpolationMode: 'default',
data,
fill: false,
hoverBackgroundColor: color,
label: legends[index],
// @ts-expect-error The typings here doesn't reflect this one
lineTension: 0.25
});
return chartData;
}, { datasets: [] as ChartDataset<'line'>[], labels });
}
function LineChart ({ className = '', colors, labels, legends, options, title, values }: Props): React.ReactElement<Props> | null {
const chartOptions = useMemo(
() => getOptions(options),
[options]
);
const chartData = useMemo(
() => getData(colors, legends, labels, values),
[colors, labels, legends, values]
);
return (
<StyledDiv className={`${className} ui--Chart-Line`}>
{title && <h1 className='ui--Chart-Header'>{title}</h1>}
<ErrorBoundary>
<Chart.Line
data={chartData}
options={chartOptions}
/>
</ErrorBoundary>
</StyledDiv>
);
}
const StyledDiv = styled.div`
h1.ui--Chart-Header {
margin-bottom: 0.25rem;
margin-top: 1rem;
padding-left: 0.25rem;
}
`;
export default React.memo(LineChart);
@@ -0,0 +1,12 @@
// Copyright 2017-2025 @pezkuwi/react-components authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { ChartType } from 'chart.js';
import type { CrosshairOptions } from 'chartjs-plugin-crosshair';
declare module 'chart.js' {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
interface PluginOptionsByType<TType extends ChartType> {
crosshair: CrosshairOptions;
}
}
@@ -0,0 +1,10 @@
// Copyright 2017-2025 @pezkuwi/react-components authors & contributors
// SPDX-License-Identifier: Apache-2.0
declare module 'chart.js/helpers' {
export const color: (c: string) => {
alpha: (a: number) => {
rgbString: () => string;
};
};
}
@@ -0,0 +1,48 @@
// Copyright 2017-2025 @pezkuwi/react-components authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { ChartType, Plugin } from 'chart.js';
import { CategoryScale, Chart, LinearScale, LineElement, PointElement, Title, Tooltip } from 'chart.js';
import annotationPlugin from 'chartjs-plugin-annotation';
import crosshairPlugin from 'chartjs-plugin-crosshair';
import Doughnut from './Doughnut.js';
import HorizBar from './HorizBar.js';
import Line from './Line.js';
interface CrosshairChart {
crosshair?: boolean;
}
function CustomCrosshairPlugin (plugin: Plugin<ChartType>): Plugin<ChartType> {
const originalAfterDraw = plugin.afterDraw;
if (originalAfterDraw) {
plugin.afterDraw = function (chart, args): void {
if ((chart as CrosshairChart).crosshair) {
// @ts-expect-error - Pass exactly what the original plugin expects
originalAfterDraw.call(this, chart, args);
}
};
}
return plugin;
}
Chart.register(
CategoryScale,
LinearScale,
LineElement,
PointElement,
Title,
Tooltip,
annotationPlugin,
CustomCrosshairPlugin(crosshairPlugin as Plugin<ChartType>)
);
export default {
Doughnut,
HorizBar,
Line
};
@@ -0,0 +1,8 @@
// Copyright 2017-2025 @pezkuwi/react-components authors & contributors
// SPDX-License-Identifier: Apache-2.0
import * as helpers from 'chart.js/helpers';
export function alphaColor (hexColor: string): string {
return helpers.color(hexColor).alpha(0.65).rgbString();
}