Skip to content

packages/core/src/lib/components/polygon.ts

Read as Markdown

This is the source snapshot used to build these API details. View this revision on GitHub.

Back to reference

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 built with @pibbl/core 0.0.2, revision 2dccb19. ALPHA — NOT FOR PRODUCTION USE.