@rowsncolumns/spreadsheet 10.6.0 → 10.6.4
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/cjs/api/cf-data-bar.d.ts +73 -0
- package/dist/cjs/api/cf-data-bar.d.ts.map +1 -0
- package/dist/cjs/api/cf-data-bar.js +182 -0
- package/dist/cjs/api/cf-icons.d.ts +73 -0
- package/dist/cjs/api/cf-icons.d.ts.map +1 -0
- package/dist/cjs/api/cf-icons.js +322 -0
- package/dist/cjs/api/cf-overlays.d.ts +89 -3
- package/dist/cjs/api/cf-overlays.d.ts.map +1 -1
- package/dist/cjs/api/cf-overlays.js +256 -80
- package/dist/cjs/api/cf-priority.d.ts +34 -0
- package/dist/cjs/api/cf-priority.d.ts.map +1 -0
- package/dist/cjs/api/cf-priority.js +94 -0
- package/dist/cjs/api/heatmap.d.ts +7 -1
- package/dist/cjs/api/heatmap.d.ts.map +1 -1
- package/dist/cjs/api/heatmap.js +27 -11
- package/dist/cjs/api/selections.d.ts +2 -2
- package/dist/cjs/api/validation.d.ts +6 -0
- package/dist/cjs/api/validation.d.ts.map +1 -1
- package/dist/cjs/api/validation.js +10 -1
- package/dist/cjs/components/canvas-grid/index.d.ts.map +1 -1
- package/dist/cjs/components/canvas-grid/index.js +194 -51
- package/dist/cjs/components/canvas-grid/overflow.d.ts +37 -0
- package/dist/cjs/components/canvas-grid/overflow.d.ts.map +1 -0
- package/dist/cjs/components/canvas-grid/overflow.js +49 -0
- package/dist/cjs/components/canvas-grid/page-breaks.d.ts +15 -0
- package/dist/cjs/components/canvas-grid/page-breaks.d.ts.map +1 -0
- package/dist/cjs/components/canvas-grid/page-breaks.js +77 -0
- package/dist/cjs/components/canvas-grid/typings.d.ts +25 -0
- package/dist/cjs/components/canvas-grid/typings.d.ts.map +1 -1
- package/dist/cjs/components/cell/alignment.d.ts +58 -1
- package/dist/cjs/components/cell/alignment.d.ts.map +1 -1
- package/dist/cjs/components/cell/alignment.js +144 -1
- package/dist/cjs/components/cell/cell-background.d.ts +2 -0
- package/dist/cjs/components/cell/cell-background.d.ts.map +1 -1
- package/dist/cjs/components/cell/cell-background.js +5 -6
- package/dist/cjs/components/cell/cell-backgrounds.d.ts.map +1 -1
- package/dist/cjs/components/cell/cell-backgrounds.js +5 -1
- package/dist/cjs/components/cell/cell-text-runs.js +1 -1
- package/dist/cjs/components/cell/index.d.ts.map +1 -1
- package/dist/cjs/components/cell/index.js +378 -37
- package/dist/cjs/components/cell/number-format-layout.d.ts +35 -0
- package/dist/cjs/components/cell/number-format-layout.d.ts.map +1 -0
- package/dist/cjs/components/cell/number-format-layout.js +106 -0
- package/dist/cjs/components/cell/typings.d.ts +22 -0
- package/dist/cjs/components/cell/typings.d.ts.map +1 -1
- package/dist/cjs/components/cell-editor/index.js +2 -2
- package/dist/cjs/components/cell-filter/index.d.ts.map +1 -1
- package/dist/cjs/components/cell-filter/index.js +3 -3
- package/dist/cjs/components/cell-overlay/index.d.ts +20 -0
- package/dist/cjs/components/cell-overlay/index.d.ts.map +1 -1
- package/dist/cjs/components/cell-overlay/index.js +155 -28
- package/dist/cjs/components/cf-icon-preview/index.d.ts +16 -0
- package/dist/cjs/components/cf-icon-preview/index.d.ts.map +1 -0
- package/dist/cjs/components/cf-icon-preview/index.js +48 -0
- package/dist/cjs/components/embeds/embed-component.d.ts.map +1 -1
- package/dist/cjs/components/embeds/embed-component.js +3 -2
- package/dist/cjs/components/hidden-markers/index.d.ts.map +1 -1
- package/dist/cjs/components/hidden-markers/index.js +8 -7
- package/dist/cjs/components/hidden-markers/style.d.ts +7 -0
- package/dist/cjs/components/hidden-markers/style.d.ts.map +1 -1
- package/dist/cjs/components/hidden-markers/style.js +13 -2
- package/dist/cjs/components/protect-sheet-dialog/index.d.ts +67 -0
- package/dist/cjs/components/protect-sheet-dialog/index.d.ts.map +1 -0
- package/dist/cjs/components/protect-sheet-dialog/index.js +196 -0
- package/dist/cjs/components/toolbar/types.d.ts +1 -1
- package/dist/cjs/components/toolbar/types.d.ts.map +1 -1
- package/dist/cjs/config.d.ts +13 -1
- package/dist/cjs/config.d.ts.map +1 -1
- package/dist/cjs/config.js +15 -3
- package/dist/cjs/helpers.d.ts +10 -5
- package/dist/cjs/helpers.d.ts.map +1 -1
- package/dist/cjs/helpers.js +43 -31
- package/dist/cjs/hooks/use-border-styles.d.ts.map +1 -1
- package/dist/cjs/hooks/use-border-styles.js +5 -28
- package/dist/cjs/hooks/use-cell-renderer.d.ts +35 -3
- package/dist/cjs/hooks/use-cell-renderer.d.ts.map +1 -1
- package/dist/cjs/hooks/use-cell-renderer.js +85 -47
- package/dist/cjs/hooks/use-conditional-formatting.d.ts +13 -1
- package/dist/cjs/hooks/use-conditional-formatting.d.ts.map +1 -1
- package/dist/cjs/hooks/use-conditional-formatting.js +281 -230
- package/dist/cjs/index.d.ts +5 -0
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +5 -0
- package/dist/cjs/release-date.js +1 -1
- package/dist/cjs/themes.d.ts +8 -0
- package/dist/cjs/themes.d.ts.map +1 -1
- package/dist/cjs/themes.js +21 -3
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/cjs/types.d.ts +81 -4
- package/dist/cjs/types.d.ts.map +1 -1
- package/dist/esm/api/cf-data-bar.d.ts +73 -0
- package/dist/esm/api/cf-data-bar.d.ts.map +1 -0
- package/dist/esm/api/cf-data-bar.js +178 -0
- package/dist/esm/api/cf-icons.d.ts +73 -0
- package/dist/esm/api/cf-icons.d.ts.map +1 -0
- package/dist/esm/api/cf-icons.js +306 -0
- package/dist/esm/api/cf-overlays.d.ts +89 -3
- package/dist/esm/api/cf-overlays.d.ts.map +1 -1
- package/dist/esm/api/cf-overlays.js +223 -67
- package/dist/esm/api/cf-priority.d.ts +34 -0
- package/dist/esm/api/cf-priority.d.ts.map +1 -0
- package/dist/esm/api/cf-priority.js +61 -0
- package/dist/esm/api/heatmap.d.ts +7 -1
- package/dist/esm/api/heatmap.d.ts.map +1 -1
- package/dist/esm/api/heatmap.js +24 -9
- package/dist/esm/api/selections.d.ts +2 -2
- package/dist/esm/api/validation.d.ts +6 -0
- package/dist/esm/api/validation.d.ts.map +1 -1
- package/dist/esm/api/validation.js +6 -0
- package/dist/esm/components/canvas-grid/index.d.ts.map +1 -1
- package/dist/esm/components/canvas-grid/index.js +163 -47
- package/dist/esm/components/canvas-grid/overflow.d.ts +37 -0
- package/dist/esm/components/canvas-grid/overflow.d.ts.map +1 -0
- package/dist/esm/components/canvas-grid/overflow.js +39 -0
- package/dist/esm/components/canvas-grid/page-breaks.d.ts +15 -0
- package/dist/esm/components/canvas-grid/page-breaks.d.ts.map +1 -0
- package/dist/esm/components/canvas-grid/page-breaks.js +50 -0
- package/dist/esm/components/canvas-grid/typings.d.ts +25 -0
- package/dist/esm/components/canvas-grid/typings.d.ts.map +1 -1
- package/dist/esm/components/cell/alignment.d.ts +58 -1
- package/dist/esm/components/cell/alignment.d.ts.map +1 -1
- package/dist/esm/components/cell/alignment.js +110 -1
- package/dist/esm/components/cell/cell-background.d.ts +2 -0
- package/dist/esm/components/cell/cell-background.d.ts.map +1 -1
- package/dist/esm/components/cell/cell-background.js +4 -5
- package/dist/esm/components/cell/cell-backgrounds.d.ts.map +1 -1
- package/dist/esm/components/cell/cell-backgrounds.js +5 -1
- package/dist/esm/components/cell/cell-text-runs.js +2 -2
- package/dist/esm/components/cell/index.d.ts.map +1 -1
- package/dist/esm/components/cell/index.js +339 -39
- package/dist/esm/components/cell/number-format-layout.d.ts +35 -0
- package/dist/esm/components/cell/number-format-layout.d.ts.map +1 -0
- package/dist/esm/components/cell/number-format-layout.js +99 -0
- package/dist/esm/components/cell/typings.d.ts +22 -0
- package/dist/esm/components/cell/typings.d.ts.map +1 -1
- package/dist/esm/components/cell-editor/index.js +2 -2
- package/dist/esm/components/cell-filter/index.d.ts.map +1 -1
- package/dist/esm/components/cell-filter/index.js +4 -4
- package/dist/esm/components/cell-overlay/index.d.ts +20 -0
- package/dist/esm/components/cell-overlay/index.d.ts.map +1 -1
- package/dist/esm/components/cell-overlay/index.js +111 -25
- package/dist/esm/components/cf-icon-preview/index.d.ts +16 -0
- package/dist/esm/components/cf-icon-preview/index.d.ts.map +1 -0
- package/dist/esm/components/cf-icon-preview/index.js +41 -0
- package/dist/esm/components/embeds/embed-component.d.ts.map +1 -1
- package/dist/esm/components/embeds/embed-component.js +4 -3
- package/dist/esm/components/hidden-markers/index.d.ts.map +1 -1
- package/dist/esm/components/hidden-markers/index.js +9 -8
- package/dist/esm/components/hidden-markers/style.d.ts +7 -0
- package/dist/esm/components/hidden-markers/style.d.ts.map +1 -1
- package/dist/esm/components/hidden-markers/style.js +8 -1
- package/dist/esm/components/protect-sheet-dialog/index.d.ts +67 -0
- package/dist/esm/components/protect-sheet-dialog/index.d.ts.map +1 -0
- package/dist/esm/components/protect-sheet-dialog/index.js +154 -0
- package/dist/esm/components/toolbar/types.d.ts +1 -1
- package/dist/esm/components/toolbar/types.d.ts.map +1 -1
- package/dist/esm/config.d.ts +13 -1
- package/dist/esm/config.d.ts.map +1 -1
- package/dist/esm/config.js +13 -1
- package/dist/esm/helpers.d.ts +10 -5
- package/dist/esm/helpers.d.ts.map +1 -1
- package/dist/esm/helpers.js +40 -28
- package/dist/esm/hooks/use-border-styles.d.ts.map +1 -1
- package/dist/esm/hooks/use-border-styles.js +5 -26
- package/dist/esm/hooks/use-cell-renderer.d.ts +35 -3
- package/dist/esm/hooks/use-cell-renderer.d.ts.map +1 -1
- package/dist/esm/hooks/use-cell-renderer.js +83 -42
- package/dist/esm/hooks/use-conditional-formatting.d.ts +13 -1
- package/dist/esm/hooks/use-conditional-formatting.d.ts.map +1 -1
- package/dist/esm/hooks/use-conditional-formatting.js +215 -120
- package/dist/esm/index.d.ts +5 -0
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +5 -0
- package/dist/esm/release-date.js +1 -1
- package/dist/esm/themes.d.ts +8 -0
- package/dist/esm/themes.d.ts.map +1 -1
- package/dist/esm/themes.js +20 -3
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/esm/types.d.ts +81 -4
- package/dist/esm/types.d.ts.map +1 -1
- package/dist/spreadsheet.min.css +1 -1
- package/package.json +4 -4
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
export type DataBarAxisMode = "automatic" | "middle" | "none";
|
|
2
|
+
export type DataBarGeometryInput = {
|
|
3
|
+
value: number;
|
|
4
|
+
/** Resolved lower bound of the rule. */
|
|
5
|
+
min: number;
|
|
6
|
+
/** Resolved upper bound of the rule. */
|
|
7
|
+
max: number;
|
|
8
|
+
/** Absent = Excel 2007 rule semantics (no axis). */
|
|
9
|
+
axisPosition?: DataBarAxisMode | null;
|
|
10
|
+
/** Percent of the cell width; default 10. */
|
|
11
|
+
minLength?: number | null;
|
|
12
|
+
/** Percent of the cell width; default 90. */
|
|
13
|
+
maxLength?: number | null;
|
|
14
|
+
direction?: "leftToRight" | "rightToLeft" | "context" | null;
|
|
15
|
+
};
|
|
16
|
+
export type DataBarGeometry = {
|
|
17
|
+
/** Left edge, 0..1 of the bar area (left-to-right, mirrored for RTL). */
|
|
18
|
+
barStart: number;
|
|
19
|
+
/** Right edge, 0..1 of the bar area. */
|
|
20
|
+
barEnd: number;
|
|
21
|
+
negative: boolean;
|
|
22
|
+
/** Axis x, 0..1 of the bar area; absent when no axis is drawn. */
|
|
23
|
+
axisPosition?: number;
|
|
24
|
+
};
|
|
25
|
+
export declare const DATA_BAR_DEFAULT_MIN_LENGTH = 10;
|
|
26
|
+
export declare const DATA_BAR_DEFAULT_MAX_LENGTH = 90;
|
|
27
|
+
/**
|
|
28
|
+
* Where a value's bar sits inside the cell. `null` when nothing is drawn
|
|
29
|
+
* (non-finite inputs).
|
|
30
|
+
*/
|
|
31
|
+
export declare const computeDataBarGeometry: ({ value, min: rawMin, max: rawMax, axisPosition, minLength, maxLength, direction, }: DataBarGeometryInput) => DataBarGeometry | null;
|
|
32
|
+
/** The canvas calls `paintDataBar` makes — satisfied by both a Konva `Context` and a 2D context wrapper. */
|
|
33
|
+
export type CfPaintContext = {
|
|
34
|
+
beginPath(): void;
|
|
35
|
+
closePath(): void;
|
|
36
|
+
moveTo(x: number, y: number): void;
|
|
37
|
+
lineTo(x: number, y: number): void;
|
|
38
|
+
stroke(): void;
|
|
39
|
+
fillRect(x: number, y: number, width: number, height: number): void;
|
|
40
|
+
strokeRect(x: number, y: number, width: number, height: number): void;
|
|
41
|
+
setLineDash(segments: number[]): void;
|
|
42
|
+
createLinearGradient(x0: number, y0: number, x1: number, y1: number): CanvasGradient;
|
|
43
|
+
setAttr(attr: string, value: unknown): void;
|
|
44
|
+
};
|
|
45
|
+
export type PaintedDataBar = {
|
|
46
|
+
color: string;
|
|
47
|
+
barStart: number;
|
|
48
|
+
barEnd: number;
|
|
49
|
+
negative?: boolean;
|
|
50
|
+
borderColor?: string;
|
|
51
|
+
gradient?: boolean;
|
|
52
|
+
axisPosition?: number;
|
|
53
|
+
axisColor?: string;
|
|
54
|
+
direction?: "leftToRight" | "rightToLeft";
|
|
55
|
+
};
|
|
56
|
+
/** Horizontal / vertical padding between the bar area and the cell edges, in px at 100 %. */
|
|
57
|
+
export declare const DATA_BAR_INSET_X = 1;
|
|
58
|
+
export declare const DATA_BAR_INSET_Y = 2;
|
|
59
|
+
/**
|
|
60
|
+
* The pale end of Excel's data-bar gradient: the bar colour blended 90 %
|
|
61
|
+
* towards white.
|
|
62
|
+
*/
|
|
63
|
+
export declare const dataBarGradientTip: (color: string) => string;
|
|
64
|
+
/**
|
|
65
|
+
* Paint one cell's data bar in the cell's local coordinates (`0,0` = the
|
|
66
|
+
* cell's top-left corner).
|
|
67
|
+
*/
|
|
68
|
+
export declare const paintDataBar: (ctx: CfPaintContext, cell: {
|
|
69
|
+
width: number;
|
|
70
|
+
height: number;
|
|
71
|
+
scale?: number;
|
|
72
|
+
}, bar: PaintedDataBar) => void;
|
|
73
|
+
//# sourceMappingURL=cf-data-bar.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cf-data-bar.d.ts","sourceRoot":"","sources":["../../../api/cf-data-bar.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
// Data-bar conditional formatting: bar geometry and canvas painting.
|
|
2
|
+
//
|
|
3
|
+
// Mirrors Excel 2010+ data bars (https://support.microsoft.com/office/
|
|
4
|
+
// use-data-bars-color-scales-and-icon-sets-to-highlight-data-
|
|
5
|
+
// f118d0b6-f19e-4ca2-8c9d-e4eac0b3b0f1, "Format all cells based on their
|
|
6
|
+
// values" > Data Bar):
|
|
7
|
+
// - bar length scales between the rule's minimum and maximum, from
|
|
8
|
+
// `minLength` to `maxLength` percent of the cell (OOXML defaults 10/90;
|
|
9
|
+
// Excel 2010+ rules write 0/100);
|
|
10
|
+
// - negative values draw on the other side of an axis ("Automatic" places
|
|
11
|
+
// the axis in proportion to the negative share of the range, "Cell
|
|
12
|
+
// midpoint" at the centre, "None" draws negative bars from the same edge
|
|
13
|
+
// as positive ones);
|
|
14
|
+
// - gradient (default) or solid fill, optional border, separate negative
|
|
15
|
+
// fill / border colours, left-to-right or right-to-left direction.
|
|
16
|
+
export const DATA_BAR_DEFAULT_MIN_LENGTH = 10;
|
|
17
|
+
export const DATA_BAR_DEFAULT_MAX_LENGTH = 90;
|
|
18
|
+
const clamp01 = (n) => Math.max(0, Math.min(1, n));
|
|
19
|
+
const lengthFractions = (minLength, maxLength) => {
|
|
20
|
+
const lo = clamp01((minLength ?? DATA_BAR_DEFAULT_MIN_LENGTH) / 100);
|
|
21
|
+
const hi = clamp01((maxLength ?? DATA_BAR_DEFAULT_MAX_LENGTH) / 100);
|
|
22
|
+
return hi < lo ? [hi, lo] : [lo, hi];
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Where a value's bar sits inside the cell. `null` when nothing is drawn
|
|
26
|
+
* (non-finite inputs).
|
|
27
|
+
*/
|
|
28
|
+
export const computeDataBarGeometry = ({ value, min: rawMin, max: rawMax, axisPosition, minLength, maxLength, direction, }) => {
|
|
29
|
+
if (!Number.isFinite(value) ||
|
|
30
|
+
!Number.isFinite(rawMin) ||
|
|
31
|
+
!Number.isFinite(rawMax)) {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
// Excel: a minimum above the maximum collapses onto the maximum.
|
|
35
|
+
const min = Math.min(rawMin, rawMax);
|
|
36
|
+
const max = rawMax;
|
|
37
|
+
const [lo, hi] = lengthFractions(minLength, maxLength);
|
|
38
|
+
const lengthFor = (fraction) => lo + clamp01(fraction) * (hi - lo);
|
|
39
|
+
const negative = value < 0;
|
|
40
|
+
const clamped = Math.max(min, Math.min(max, value));
|
|
41
|
+
const mode = axisPosition ?? "none";
|
|
42
|
+
let barStart = 0;
|
|
43
|
+
let barEnd = 0;
|
|
44
|
+
let axis;
|
|
45
|
+
if (mode === "middle") {
|
|
46
|
+
axis = 0.5;
|
|
47
|
+
const magnitude = Math.max(Math.abs(min), Math.abs(max));
|
|
48
|
+
const length = 0.5 * lengthFor(magnitude > 0 ? Math.abs(clamped) / magnitude : 1);
|
|
49
|
+
if (negative) {
|
|
50
|
+
barStart = 0.5 - length;
|
|
51
|
+
barEnd = 0.5;
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
barStart = 0.5;
|
|
55
|
+
barEnd = 0.5 + length;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
else if (mode === "automatic" && min < 0 && max > 0) {
|
|
59
|
+
axis = -min / (max - min);
|
|
60
|
+
if (negative) {
|
|
61
|
+
const length = axis * lengthFor(clamped / min);
|
|
62
|
+
barStart = axis - length;
|
|
63
|
+
barEnd = axis;
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
const length = (1 - axis) * lengthFor(clamped / max);
|
|
67
|
+
barStart = axis;
|
|
68
|
+
barEnd = axis + length;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
else if (mode === "automatic" && max <= 0 && min < 0) {
|
|
72
|
+
// Only negative values: the axis sits on the right edge and every bar
|
|
73
|
+
// grows leftwards from it.
|
|
74
|
+
axis = 1;
|
|
75
|
+
const span = max - min;
|
|
76
|
+
const length = lengthFor(span > 0 ? (max - clamped) / span : 1);
|
|
77
|
+
barStart = 1 - length;
|
|
78
|
+
barEnd = 1;
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
// No axis (or Automatic with no negative bound): every bar grows from
|
|
82
|
+
// the left edge, scaled between min and max.
|
|
83
|
+
const span = max - min;
|
|
84
|
+
barStart = 0;
|
|
85
|
+
barEnd = lengthFor(span > 0 ? (clamped - min) / span : 1);
|
|
86
|
+
}
|
|
87
|
+
if (direction === "rightToLeft") {
|
|
88
|
+
const start = 1 - barEnd;
|
|
89
|
+
barEnd = 1 - barStart;
|
|
90
|
+
barStart = start;
|
|
91
|
+
if (axis !== undefined)
|
|
92
|
+
axis = 1 - axis;
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
barStart: clamp01(barStart),
|
|
96
|
+
barEnd: clamp01(barEnd),
|
|
97
|
+
negative,
|
|
98
|
+
...(axis !== undefined ? { axisPosition: axis } : {}),
|
|
99
|
+
};
|
|
100
|
+
};
|
|
101
|
+
/** Horizontal / vertical padding between the bar area and the cell edges, in px at 100 %. */
|
|
102
|
+
export const DATA_BAR_INSET_X = 1;
|
|
103
|
+
export const DATA_BAR_INSET_Y = 2;
|
|
104
|
+
const parseHex = (color) => {
|
|
105
|
+
const m = /^#?([0-9a-f]{6})([0-9a-f]{2})?$/i.exec(color.trim());
|
|
106
|
+
if (m) {
|
|
107
|
+
const n = parseInt(m[1], 16);
|
|
108
|
+
return [(n >> 16) & 255, (n >> 8) & 255, n & 255];
|
|
109
|
+
}
|
|
110
|
+
const rgb = /^rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)/i.exec(color.trim());
|
|
111
|
+
if (rgb)
|
|
112
|
+
return [Number(rgb[1]), Number(rgb[2]), Number(rgb[3])];
|
|
113
|
+
return null;
|
|
114
|
+
};
|
|
115
|
+
/**
|
|
116
|
+
* The pale end of Excel's data-bar gradient: the bar colour blended 90 %
|
|
117
|
+
* towards white.
|
|
118
|
+
*/
|
|
119
|
+
export const dataBarGradientTip = (color) => {
|
|
120
|
+
const rgb = parseHex(color);
|
|
121
|
+
if (!rgb)
|
|
122
|
+
return "#ffffff";
|
|
123
|
+
const blend = (c) => Math.round(c + (255 - c) * 0.9);
|
|
124
|
+
return `rgb(${blend(rgb[0])}, ${blend(rgb[1])}, ${blend(rgb[2])})`;
|
|
125
|
+
};
|
|
126
|
+
/**
|
|
127
|
+
* Paint one cell's data bar in the cell's local coordinates (`0,0` = the
|
|
128
|
+
* cell's top-left corner).
|
|
129
|
+
*/
|
|
130
|
+
export const paintDataBar = (ctx, cell, bar) => {
|
|
131
|
+
const scale = cell.scale ?? 1;
|
|
132
|
+
const areaX = DATA_BAR_INSET_X * scale;
|
|
133
|
+
const areaY = DATA_BAR_INSET_Y * scale;
|
|
134
|
+
const areaWidth = Math.max(0, cell.width - 2 * DATA_BAR_INSET_X * scale);
|
|
135
|
+
const areaHeight = Math.max(0, cell.height - 2 * DATA_BAR_INSET_Y * scale);
|
|
136
|
+
if (areaWidth <= 0 || areaHeight <= 0)
|
|
137
|
+
return;
|
|
138
|
+
const left = areaX + clamp01(bar.barStart) * areaWidth;
|
|
139
|
+
const right = areaX + clamp01(bar.barEnd) * areaWidth;
|
|
140
|
+
const barWidth = right - left;
|
|
141
|
+
if (barWidth > 0) {
|
|
142
|
+
if (bar.gradient === false) {
|
|
143
|
+
ctx.setAttr("fillStyle", bar.color);
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
// The gradient runs from the bar's base (the axis, or the edge it
|
|
147
|
+
// grows from) to its tip.
|
|
148
|
+
const rtl = bar.direction === "rightToLeft";
|
|
149
|
+
const hasAxis = bar.axisPosition !== undefined;
|
|
150
|
+
const growsLeft = (!!bar.negative && hasAxis) !== rtl;
|
|
151
|
+
const gradient = growsLeft
|
|
152
|
+
? ctx.createLinearGradient(right, 0, left, 0)
|
|
153
|
+
: ctx.createLinearGradient(left, 0, right, 0);
|
|
154
|
+
gradient.addColorStop(0, bar.color);
|
|
155
|
+
gradient.addColorStop(1, dataBarGradientTip(bar.color));
|
|
156
|
+
ctx.setAttr("fillStyle", gradient);
|
|
157
|
+
}
|
|
158
|
+
ctx.fillRect(left, areaY, barWidth, areaHeight);
|
|
159
|
+
if (bar.borderColor) {
|
|
160
|
+
ctx.setLineDash([]);
|
|
161
|
+
ctx.setAttr("strokeStyle", bar.borderColor);
|
|
162
|
+
ctx.setAttr("lineWidth", scale);
|
|
163
|
+
ctx.strokeRect(left + 0.5, areaY + 0.5, Math.max(0, barWidth - 1), Math.max(0, areaHeight - 1));
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
if (bar.axisPosition !== undefined) {
|
|
167
|
+
const axisX = Math.round(areaX + clamp01(bar.axisPosition) * areaWidth) + 0.5;
|
|
168
|
+
ctx.beginPath();
|
|
169
|
+
ctx.setLineDash([scale, scale]);
|
|
170
|
+
ctx.setAttr("strokeStyle", bar.axisColor ?? "#000000");
|
|
171
|
+
ctx.setAttr("lineWidth", scale);
|
|
172
|
+
ctx.moveTo(axisX, 0);
|
|
173
|
+
ctx.lineTo(axisX, cell.height);
|
|
174
|
+
ctx.stroke();
|
|
175
|
+
ctx.setLineDash([]);
|
|
176
|
+
ctx.closePath();
|
|
177
|
+
}
|
|
178
|
+
};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { IconSetFamily } from "../types";
|
|
2
|
+
export type CfIconShape = {
|
|
3
|
+
kind: "arrow";
|
|
4
|
+
angle: number;
|
|
5
|
+
color: string;
|
|
6
|
+
} | {
|
|
7
|
+
kind: "circle";
|
|
8
|
+
color: string;
|
|
9
|
+
} | {
|
|
10
|
+
kind: "rimmedCircle";
|
|
11
|
+
color: string;
|
|
12
|
+
} | {
|
|
13
|
+
kind: "flag";
|
|
14
|
+
color: string;
|
|
15
|
+
} | {
|
|
16
|
+
kind: "diamond";
|
|
17
|
+
color: string;
|
|
18
|
+
} | {
|
|
19
|
+
kind: "triangleUp";
|
|
20
|
+
color: string;
|
|
21
|
+
} | {
|
|
22
|
+
kind: "triangleDown";
|
|
23
|
+
color: string;
|
|
24
|
+
} | {
|
|
25
|
+
kind: "dash";
|
|
26
|
+
color: string;
|
|
27
|
+
} | {
|
|
28
|
+
kind: "symbol";
|
|
29
|
+
glyph: "check" | "exclamation" | "cross";
|
|
30
|
+
color: string;
|
|
31
|
+
} | {
|
|
32
|
+
kind: "symbolPlain";
|
|
33
|
+
glyph: "check" | "exclamation" | "cross";
|
|
34
|
+
color: string;
|
|
35
|
+
} | {
|
|
36
|
+
kind: "star";
|
|
37
|
+
fill: 0 | 0.5 | 1;
|
|
38
|
+
} | {
|
|
39
|
+
kind: "rating";
|
|
40
|
+
filled: number;
|
|
41
|
+
} | {
|
|
42
|
+
kind: "quarters";
|
|
43
|
+
filled: number;
|
|
44
|
+
} | {
|
|
45
|
+
kind: "boxes";
|
|
46
|
+
filled: number;
|
|
47
|
+
};
|
|
48
|
+
/** Every icon set, lowest bucket first (OOXML `iconId` order). */
|
|
49
|
+
export declare const CF_ICON_SETS: Record<IconSetFamily, CfIconShape[]>;
|
|
50
|
+
/** Families Excel only stores in the x14 extension (no Excel 2007 equivalent). */
|
|
51
|
+
export declare const X14_ONLY_ICON_SETS: ReadonlySet<IconSetFamily>;
|
|
52
|
+
export declare const getCfIcon: (iconSet: string | null | undefined, iconId: number | null | undefined) => CfIconShape | undefined;
|
|
53
|
+
/** The canvas calls the icon painter makes — satisfied by a Konva `Context`. */
|
|
54
|
+
export type CfIconPaintContext = {
|
|
55
|
+
save(): void;
|
|
56
|
+
restore(): void;
|
|
57
|
+
beginPath(): void;
|
|
58
|
+
closePath(): void;
|
|
59
|
+
moveTo(x: number, y: number): void;
|
|
60
|
+
lineTo(x: number, y: number): void;
|
|
61
|
+
arc(x: number, y: number, radius: number, startAngle: number, endAngle: number, counterClockwise?: boolean): void;
|
|
62
|
+
rect(x: number, y: number, width: number, height: number): void;
|
|
63
|
+
fill(): void;
|
|
64
|
+
stroke(): void;
|
|
65
|
+
clip(): void;
|
|
66
|
+
setAttr(attr: string, value: unknown): void;
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* Paint `icon` into the square at (`x`, `y`) with side `size` (local canvas
|
|
70
|
+
* coordinates).
|
|
71
|
+
*/
|
|
72
|
+
export declare const paintCfIcon: (ctx: CfIconPaintContext, icon: CfIconShape, x: number, y: number, size: number) => void;
|
|
73
|
+
//# sourceMappingURL=cf-icons.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cf-icons.d.ts","sourceRoot":"","sources":["../../../api/cf-icons.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
// Conditional-format icon sets drawn as small vector icons on the canvas.
|
|
2
|
+
//
|
|
3
|
+
// Excel's 20 icon sets (https://support.microsoft.com/office/
|
|
4
|
+
// use-data-bars-color-scales-and-icon-sets-to-highlight-data-
|
|
5
|
+
// f118d0b6-f19e-4ca2-8c9d-e4eac0b3b0f1, "Icon sets"). Each set lists its
|
|
6
|
+
// icons in OOXML `iconId` order — index 0 is the icon for the LOWEST bucket —
|
|
7
|
+
// so `iconId` matches what `<iconFilter>` / `<x14:cfIcon>` store. Each icon
|
|
8
|
+
// is described once in a 16 × 16 design box and painted with plain canvas
|
|
9
|
+
// paths, so it scales with zoom and needs no font or image asset.
|
|
10
|
+
const RED = "#E5383B";
|
|
11
|
+
const YELLOW = "#F2C12E";
|
|
12
|
+
const GREEN = "#2BA84A";
|
|
13
|
+
const GRAY = "#7F7F7F";
|
|
14
|
+
const BLACK = "#262626";
|
|
15
|
+
const PINK = "#F4A6A6";
|
|
16
|
+
const DARK_GRAY = "#8C8C8C";
|
|
17
|
+
const GOLD = "#F5B800";
|
|
18
|
+
const BLUE = "#2F6EBA";
|
|
19
|
+
const EMPTY = "#D9D9D9";
|
|
20
|
+
const UP = 0;
|
|
21
|
+
const UP_RIGHT = 45;
|
|
22
|
+
const RIGHT = 90;
|
|
23
|
+
const DOWN_RIGHT = 135;
|
|
24
|
+
const DOWN = 180;
|
|
25
|
+
const arrow = (angle, color) => ({
|
|
26
|
+
kind: "arrow",
|
|
27
|
+
angle,
|
|
28
|
+
color,
|
|
29
|
+
});
|
|
30
|
+
const circle = (color) => ({ kind: "circle", color });
|
|
31
|
+
/** Every icon set, lowest bucket first (OOXML `iconId` order). */
|
|
32
|
+
export const CF_ICON_SETS = {
|
|
33
|
+
"3Arrows": [arrow(DOWN, RED), arrow(RIGHT, YELLOW), arrow(UP, GREEN)],
|
|
34
|
+
"3ArrowsGray": [arrow(DOWN, GRAY), arrow(RIGHT, GRAY), arrow(UP, GRAY)],
|
|
35
|
+
"3Flags": [
|
|
36
|
+
{ kind: "flag", color: RED },
|
|
37
|
+
{ kind: "flag", color: YELLOW },
|
|
38
|
+
{ kind: "flag", color: GREEN },
|
|
39
|
+
],
|
|
40
|
+
"3TrafficLights1": [circle(RED), circle(YELLOW), circle(GREEN)],
|
|
41
|
+
"3TrafficLights2": [
|
|
42
|
+
{ kind: "rimmedCircle", color: RED },
|
|
43
|
+
{ kind: "rimmedCircle", color: YELLOW },
|
|
44
|
+
{ kind: "rimmedCircle", color: GREEN },
|
|
45
|
+
],
|
|
46
|
+
"3Signs": [
|
|
47
|
+
{ kind: "diamond", color: RED },
|
|
48
|
+
{ kind: "triangleUp", color: YELLOW },
|
|
49
|
+
circle(GREEN),
|
|
50
|
+
],
|
|
51
|
+
"3Symbols": [
|
|
52
|
+
{ kind: "symbol", glyph: "cross", color: RED },
|
|
53
|
+
{ kind: "symbol", glyph: "exclamation", color: YELLOW },
|
|
54
|
+
{ kind: "symbol", glyph: "check", color: GREEN },
|
|
55
|
+
],
|
|
56
|
+
"3Symbols2": [
|
|
57
|
+
{ kind: "symbolPlain", glyph: "cross", color: RED },
|
|
58
|
+
{ kind: "symbolPlain", glyph: "exclamation", color: YELLOW },
|
|
59
|
+
{ kind: "symbolPlain", glyph: "check", color: GREEN },
|
|
60
|
+
],
|
|
61
|
+
"3Stars": [
|
|
62
|
+
{ kind: "star", fill: 0 },
|
|
63
|
+
{ kind: "star", fill: 0.5 },
|
|
64
|
+
{ kind: "star", fill: 1 },
|
|
65
|
+
],
|
|
66
|
+
"3Triangles": [
|
|
67
|
+
{ kind: "triangleDown", color: RED },
|
|
68
|
+
{ kind: "dash", color: YELLOW },
|
|
69
|
+
{ kind: "triangleUp", color: GREEN },
|
|
70
|
+
],
|
|
71
|
+
"4Arrows": [
|
|
72
|
+
arrow(DOWN, RED),
|
|
73
|
+
arrow(DOWN_RIGHT, YELLOW),
|
|
74
|
+
arrow(UP_RIGHT, YELLOW),
|
|
75
|
+
arrow(UP, GREEN),
|
|
76
|
+
],
|
|
77
|
+
"4ArrowsGray": [
|
|
78
|
+
arrow(DOWN, GRAY),
|
|
79
|
+
arrow(DOWN_RIGHT, GRAY),
|
|
80
|
+
arrow(UP_RIGHT, GRAY),
|
|
81
|
+
arrow(UP, GRAY),
|
|
82
|
+
],
|
|
83
|
+
"4RedToBlack": [circle(BLACK), circle(DARK_GRAY), circle(PINK), circle(RED)],
|
|
84
|
+
"4Rating": [1, 2, 3, 4].map((filled) => ({ kind: "rating", filled })),
|
|
85
|
+
"4TrafficLights": [circle(BLACK), circle(RED), circle(YELLOW), circle(GREEN)],
|
|
86
|
+
"5Arrows": [
|
|
87
|
+
arrow(DOWN, RED),
|
|
88
|
+
arrow(DOWN_RIGHT, YELLOW),
|
|
89
|
+
arrow(RIGHT, YELLOW),
|
|
90
|
+
arrow(UP_RIGHT, YELLOW),
|
|
91
|
+
arrow(UP, GREEN),
|
|
92
|
+
],
|
|
93
|
+
"5ArrowsGray": [
|
|
94
|
+
arrow(DOWN, GRAY),
|
|
95
|
+
arrow(DOWN_RIGHT, GRAY),
|
|
96
|
+
arrow(RIGHT, GRAY),
|
|
97
|
+
arrow(UP_RIGHT, GRAY),
|
|
98
|
+
arrow(UP, GRAY),
|
|
99
|
+
],
|
|
100
|
+
"5Rating": [0, 1, 2, 3, 4].map((filled) => ({ kind: "rating", filled })),
|
|
101
|
+
"5Quarters": [0, 1, 2, 3, 4].map((filled) => ({ kind: "quarters", filled })),
|
|
102
|
+
"5Boxes": [0, 1, 2, 3, 4].map((filled) => ({ kind: "boxes", filled })),
|
|
103
|
+
};
|
|
104
|
+
/** Families Excel only stores in the x14 extension (no Excel 2007 equivalent). */
|
|
105
|
+
export const X14_ONLY_ICON_SETS = new Set([
|
|
106
|
+
"3Stars",
|
|
107
|
+
"3Triangles",
|
|
108
|
+
"5Boxes",
|
|
109
|
+
]);
|
|
110
|
+
export const getCfIcon = (iconSet, iconId) => iconSet && iconId != null
|
|
111
|
+
? CF_ICON_SETS[iconSet]?.[iconId]
|
|
112
|
+
: undefined;
|
|
113
|
+
const ARROW_POINTS = [
|
|
114
|
+
[8, 1],
|
|
115
|
+
[15, 8],
|
|
116
|
+
[11, 8],
|
|
117
|
+
[11, 15],
|
|
118
|
+
[5, 15],
|
|
119
|
+
[5, 8],
|
|
120
|
+
[1, 8],
|
|
121
|
+
];
|
|
122
|
+
const STAR_POINTS = Array.from({ length: 10 }, (_, i) => {
|
|
123
|
+
const radius = i % 2 === 0 ? 7.5 : 3.2;
|
|
124
|
+
const angle = -Math.PI / 2 + (i * Math.PI) / 5;
|
|
125
|
+
return [8 + radius * Math.cos(angle), 8.6 + radius * Math.sin(angle)];
|
|
126
|
+
});
|
|
127
|
+
/**
|
|
128
|
+
* Paint `icon` into the square at (`x`, `y`) with side `size` (local canvas
|
|
129
|
+
* coordinates).
|
|
130
|
+
*/
|
|
131
|
+
export const paintCfIcon = (ctx, icon, x, y, size) => {
|
|
132
|
+
const k = size / 16;
|
|
133
|
+
const px = (u) => x + u * k;
|
|
134
|
+
const py = (v) => y + v * k;
|
|
135
|
+
const polygon = (points) => {
|
|
136
|
+
ctx.beginPath();
|
|
137
|
+
points.forEach(([u, v], i) => i === 0 ? ctx.moveTo(px(u), py(v)) : ctx.lineTo(px(u), py(v)));
|
|
138
|
+
ctx.closePath();
|
|
139
|
+
};
|
|
140
|
+
const fillWith = (color) => {
|
|
141
|
+
ctx.setAttr("fillStyle", color);
|
|
142
|
+
ctx.fill();
|
|
143
|
+
};
|
|
144
|
+
const strokeWith = (color, width) => {
|
|
145
|
+
ctx.setAttr("strokeStyle", color);
|
|
146
|
+
ctx.setAttr("lineWidth", width * k);
|
|
147
|
+
ctx.setAttr("lineCap", "round");
|
|
148
|
+
ctx.setAttr("lineJoin", "round");
|
|
149
|
+
ctx.stroke();
|
|
150
|
+
};
|
|
151
|
+
const disc = (radius) => {
|
|
152
|
+
ctx.beginPath();
|
|
153
|
+
ctx.arc(px(8), py(8), radius * k, 0, Math.PI * 2);
|
|
154
|
+
ctx.closePath();
|
|
155
|
+
};
|
|
156
|
+
const glyph = (kind, color, width) => {
|
|
157
|
+
ctx.beginPath();
|
|
158
|
+
if (kind === "check") {
|
|
159
|
+
ctx.moveTo(px(4.5), py(8.5));
|
|
160
|
+
ctx.lineTo(px(7), py(11));
|
|
161
|
+
ctx.lineTo(px(11.5), py(5));
|
|
162
|
+
}
|
|
163
|
+
else if (kind === "cross") {
|
|
164
|
+
ctx.moveTo(px(5), py(5));
|
|
165
|
+
ctx.lineTo(px(11), py(11));
|
|
166
|
+
ctx.moveTo(px(11), py(5));
|
|
167
|
+
ctx.lineTo(px(5), py(11));
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
ctx.moveTo(px(8), py(3.5));
|
|
171
|
+
ctx.lineTo(px(8), py(9));
|
|
172
|
+
ctx.moveTo(px(8), py(12));
|
|
173
|
+
ctx.lineTo(px(8), py(12.2));
|
|
174
|
+
}
|
|
175
|
+
strokeWith(color, width);
|
|
176
|
+
};
|
|
177
|
+
switch (icon.kind) {
|
|
178
|
+
case "arrow": {
|
|
179
|
+
const rad = (icon.angle * Math.PI) / 180;
|
|
180
|
+
const cos = Math.cos(rad);
|
|
181
|
+
const sin = Math.sin(rad);
|
|
182
|
+
polygon(ARROW_POINTS.map(([u, v]) => [
|
|
183
|
+
8 + (u - 8) * cos - (v - 8) * sin,
|
|
184
|
+
8 + (u - 8) * sin + (v - 8) * cos,
|
|
185
|
+
]));
|
|
186
|
+
fillWith(icon.color);
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
case "circle":
|
|
190
|
+
disc(7);
|
|
191
|
+
fillWith(icon.color);
|
|
192
|
+
return;
|
|
193
|
+
case "rimmedCircle":
|
|
194
|
+
ctx.beginPath();
|
|
195
|
+
ctx.rect(px(0.5), py(0.5), 15 * k, 15 * k);
|
|
196
|
+
ctx.closePath();
|
|
197
|
+
fillWith(BLACK);
|
|
198
|
+
disc(5.5);
|
|
199
|
+
fillWith(icon.color);
|
|
200
|
+
return;
|
|
201
|
+
case "flag":
|
|
202
|
+
ctx.beginPath();
|
|
203
|
+
ctx.rect(px(3), py(1), 1.5 * k, 14 * k);
|
|
204
|
+
ctx.closePath();
|
|
205
|
+
fillWith(BLACK);
|
|
206
|
+
polygon([
|
|
207
|
+
[4.5, 1.5],
|
|
208
|
+
[14, 5.25],
|
|
209
|
+
[4.5, 9],
|
|
210
|
+
]);
|
|
211
|
+
fillWith(icon.color);
|
|
212
|
+
return;
|
|
213
|
+
case "diamond":
|
|
214
|
+
polygon([
|
|
215
|
+
[8, 1],
|
|
216
|
+
[15, 8],
|
|
217
|
+
[8, 15],
|
|
218
|
+
[1, 8],
|
|
219
|
+
]);
|
|
220
|
+
fillWith(icon.color);
|
|
221
|
+
return;
|
|
222
|
+
case "triangleUp":
|
|
223
|
+
polygon([
|
|
224
|
+
[8, 2],
|
|
225
|
+
[15, 14],
|
|
226
|
+
[1, 14],
|
|
227
|
+
]);
|
|
228
|
+
fillWith(icon.color);
|
|
229
|
+
return;
|
|
230
|
+
case "triangleDown":
|
|
231
|
+
polygon([
|
|
232
|
+
[1, 2],
|
|
233
|
+
[15, 2],
|
|
234
|
+
[8, 14],
|
|
235
|
+
]);
|
|
236
|
+
fillWith(icon.color);
|
|
237
|
+
return;
|
|
238
|
+
case "dash":
|
|
239
|
+
ctx.beginPath();
|
|
240
|
+
ctx.rect(px(2), py(6), 12 * k, 4 * k);
|
|
241
|
+
ctx.closePath();
|
|
242
|
+
fillWith(icon.color);
|
|
243
|
+
return;
|
|
244
|
+
case "symbol":
|
|
245
|
+
disc(7.5);
|
|
246
|
+
fillWith(icon.color);
|
|
247
|
+
glyph(icon.glyph, "#FFFFFF", 2);
|
|
248
|
+
return;
|
|
249
|
+
case "symbolPlain":
|
|
250
|
+
glyph(icon.glyph, icon.color, 2.6);
|
|
251
|
+
return;
|
|
252
|
+
case "star":
|
|
253
|
+
if (icon.fill > 0) {
|
|
254
|
+
ctx.save();
|
|
255
|
+
if (icon.fill < 1) {
|
|
256
|
+
ctx.beginPath();
|
|
257
|
+
ctx.rect(px(0), py(0), 8 * k, 16 * k);
|
|
258
|
+
ctx.clip();
|
|
259
|
+
}
|
|
260
|
+
polygon(STAR_POINTS);
|
|
261
|
+
fillWith(GOLD);
|
|
262
|
+
ctx.restore();
|
|
263
|
+
}
|
|
264
|
+
polygon(STAR_POINTS);
|
|
265
|
+
strokeWith(GOLD, 1);
|
|
266
|
+
return;
|
|
267
|
+
case "rating":
|
|
268
|
+
for (let bar = 0; bar < 4; bar++) {
|
|
269
|
+
const height = 4 + bar * 3.5;
|
|
270
|
+
ctx.beginPath();
|
|
271
|
+
ctx.rect(px(1 + bar * 3.75), py(15 - height), 2.75 * k, height * k);
|
|
272
|
+
ctx.closePath();
|
|
273
|
+
fillWith(bar < icon.filled ? BLUE : EMPTY);
|
|
274
|
+
}
|
|
275
|
+
return;
|
|
276
|
+
case "quarters":
|
|
277
|
+
disc(7);
|
|
278
|
+
fillWith("#FFFFFF");
|
|
279
|
+
if (icon.filled > 0) {
|
|
280
|
+
ctx.beginPath();
|
|
281
|
+
ctx.moveTo(px(8), py(8));
|
|
282
|
+
ctx.arc(px(8), py(8), 7 * k, -Math.PI / 2, -Math.PI / 2 + (icon.filled / 4) * Math.PI * 2);
|
|
283
|
+
ctx.closePath();
|
|
284
|
+
fillWith(BLACK);
|
|
285
|
+
}
|
|
286
|
+
disc(7);
|
|
287
|
+
strokeWith(BLACK, 1);
|
|
288
|
+
return;
|
|
289
|
+
case "boxes": {
|
|
290
|
+
// Quadrants fill clockwise from the top-left.
|
|
291
|
+
const cells = [
|
|
292
|
+
[1.5, 1.5],
|
|
293
|
+
[8.5, 1.5],
|
|
294
|
+
[8.5, 8.5],
|
|
295
|
+
[1.5, 8.5],
|
|
296
|
+
];
|
|
297
|
+
cells.forEach(([u, v], i) => {
|
|
298
|
+
ctx.beginPath();
|
|
299
|
+
ctx.rect(px(u), py(v), 6 * k, 6 * k);
|
|
300
|
+
ctx.closePath();
|
|
301
|
+
fillWith(i < icon.filled ? BLUE : EMPTY);
|
|
302
|
+
});
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
};
|