packages/core/src/lib/components/polygon.ts
This is the source snapshot used to build these API details. View this revision on GitHub.
1 import { definePrimitive } from '../define-primitive.js';
2 import type {
3 PibblEventFocusOptions,
4 PibblEventHandlers,
5 PibblEventParticipationOptions,
6 } from '../events/types.js';
7 import {
8 RenderingContext2D,
9 SystemStyle,
10 } from '../types.js';
11 import type { PibblPhysics2DPropExtension } from '../types.js';
12 import { stripLayoutParticipation } from '../style/resolve-built-in.js';
13 import { createPolygonPath } from '../geometry/polygon.js';
14 import { layoutDiagnostic } from '../style/diagnostics.js';
15 import { registerOpaqueStyleProperties } from '../style/resolve-dispatch.js';
16 import type {
17 BoxStyle,
18 CursorStyle,
19 LayoutItemStyle,
20 PaintStyle,
21 RoundedCornerStyle,
22 ResolvedDrawingStyle,
23 } from '../style/types.js';
24 import type { StyleResolutionContext } from '../types.js';
25 import { renderPath } from './path.js';
26 import { resolveRoundedCornerStyle } from './rounded-corner-style.js';
27
28 /**
29 * Authored inputs for Polygon, including the declared data and presentation options.
30 *
31 * @see {@link PibblEventHandlers}
32 * @see {@link PibblEventParticipationOptions}
33 * @see {@link PibblPhysics2DPropExtension}
34 * @see {@link Polygon}
35 */
36 export interface PolygonProps extends PibblEventHandlers, PibblEventFocusOptions, PibblEventParticipationOptions, PibblPhysics2DPropExtension {}
37
38 /**
39 * Supported geometry and presentation properties for Polygon.
40 *
41 * @see {@link RoundedCornerStyle}
42 * @see {@link SystemStyle}
43 * @see {@link PaintStyle}
44 * @see {@link CursorStyle}
45 * @see {@link BoxStyle}
46 * @see {@link LayoutItemStyle}
47 * @see {@link Polygon}
48 */
49 export interface PolygonStyle
50 extends
51 RoundedCornerStyle,
52 SystemStyle,
53 PaintStyle,
54 CursorStyle,
55 BoxStyle,
56 LayoutItemStyle {
57 /** Ordered coordinate pairs describing the path vertices. See {@link PolygonStyle}. */
58 coords: [number, number][];
59 /**
60 * Width used for line hit testing independently of the visible stroke width. See
61 * {@link PolygonStyle}.
62 */
63 lineInteractionWidth?: number;
64 }
65
66 type ResolvedPolygonStyle = Omit<
67 ResolvedDrawingStyle<PolygonStyle>,
68 keyof RoundedCornerStyle
69 > & {
70 coords: [number, number][];
71 cornerRadius?: number;
72 cornerRadii?: readonly (number | null | undefined)[];
73 };
74
75 function renderPolygon(
76 props: PolygonProps,
77 style: Readonly<ResolvedPolygonStyle>,
78 ctx: RenderingContext2D,
79 ) {
80 const {
81 coords,
82 fill,
83 stroke,
84 strokeWidth,
85 opacity,
86 cursor,
87 lineInteractionWidth,
88 cornerRadius,
89 cornerRadii,
90 } = style;
91 const path = createPolygonPath({ coords, cornerRadius, cornerRadii });
92
93 return renderPath(
94 {
95 ...props,
96 keyboardNavigationBounds: props.keyboardNavigationBounds ??
97 polygonBounds(coords),
98 },
99 {
100 d: path,
101 fill,
102 stroke,
103 strokeWidth,
104 opacity,
105 cursor,
106 lineInteractionWidth,
107 },
108 ctx,
109 );
110 }
111
112 /**
113 * Describes a polygon primitive for JSX or createElement authoring.
114 *
115 * @param props - Authored component inputs, supplied through JSX or createElement. See the linked
116 * props and style types.
117 * @throws When called directly; Pibbl mounts this component through JSX or createElement.
118 *
119 * @see {@link PolygonProps}
120 * @see {@link PolygonStyle}
121 */
122 export const Polygon = definePrimitive<
123 PolygonProps,
124 PolygonStyle,
125 PolygonStyle,
126 ResolvedPolygonStyle
127 >(renderPolygon, {
128 resolveStyle: (style, context) => {
129 const coords = validatePolygonCoords(style.coords, context);
130 return {
131 ...stripLayoutParticipation(style),
132 coords,
133 ...resolveRoundedCornerStyle(coords.length, style, context),
134 } as ResolvedPolygonStyle;
135 },
136 });
137 registerOpaqueStyleProperties(Polygon, ['fill', 'stroke', 'cursor']);
138
139 function validatePolygonCoords(
140 coords: [number, number][],
141 context: Readonly<StyleResolutionContext>,
142 ) {
143 for (let index = 0; index < coords.length; index++) {
144 const [x, y] = coords[index];
145 if (!Number.isFinite(x)) {
146 throw layoutDiagnostic({
147 component: context.component,
148 property: `coords[${index}][0]`,
149 value: x,
150 algorithm: context.algorithm,
151 constraints: context.constraints,
152 reason: 'must be a finite number',
153 });
154 }
155 if (!Number.isFinite(y)) {
156 throw layoutDiagnostic({
157 component: context.component,
158 property: `coords[${index}][1]`,
159 value: y,
160 algorithm: context.algorithm,
161 constraints: context.constraints,
162 reason: 'must be a finite number',
163 });
164 }
165 }
166 return coords;
167 }
168
169 function polygonBounds(coords: readonly [number, number][]) {
170 if (coords.length === 0) return undefined;
171 const xs = coords.map(([x]) => x);
172 const ys = coords.map(([, y]) => y);
173 const left = Math.min(...xs);
174 const right = Math.max(...xs);
175 const top = Math.min(...ys);
176 const bottom = Math.max(...ys);
177 return {
178 x: left,
179 y: top,
180 width: right - left,
181 height: bottom - top,
182 };
183 }
184
Documentation version
Section titled “Documentation version”Documentation built with @pibbl/core 0.0.2, revision 2dccb19. ALPHA — NOT FOR PRODUCTION USE.