Skip to content

packages/core/src/lib/components/line.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 type { HorizontalLineStyle } from './horizontal-line.js';
2 import type { VerticalLineStyle } from './vertical-line.js';
3 import { definePrimitive } from '../define-primitive.js';
4 import type {
5   PibblEventFocusOptions,
6   PibblEventHandlers,
7   PibblEventParticipationOptions,
8 } from '../events/types.js';
9 import {
10   RenderingContext2D,
11   SystemStyle,
12 } from '../types.js';
13 import type { PibblPhysics2DPropExtension } from '../types.js';
14 import { stripLayoutParticipation } from '../style/resolve-built-in.js';
15 import { registerOpaqueStyleProperties } from '../style/resolve-dispatch.js';
16 import type {
17   BoxStyle,
18   CursorStyle,
19   LayoutItemStyle,
20   PaintStyle,
21   ResolvedDrawingStyle,
22 } from '../style/types.js';
23 import { layoutDiagnostic } from '../style/diagnostics.js';
24 import { createLinePath } from '../geometry/line.js';
25 import { validateCoordinates } from '../geometry/validation.js';
26 import type { StyleResolutionContext } from '../types.js';
27 import { renderPath } from './path.js';
28 
29 /**
30  * Authored inputs for Line, including the declared data and presentation options.
31  *
32  * @see {@link PibblEventHandlers}
33  * @see {@link PibblEventParticipationOptions}
34  * @see {@link PibblPhysics2DPropExtension}
35  * @see {@link Line}
36  */
37 export interface LineProps extends PibblEventHandlers, PibblEventFocusOptions, PibblEventParticipationOptions, PibblPhysics2DPropExtension {}
38 
39 /**
40  * Supported geometry and presentation properties for Line.
41  *
42  * @see {@link SystemStyle}
43  * @see {@link BoxStyle}
44  * @see {@link LayoutItemStyle}
45  * @see {@link PaintStyle}
46  * @see {@link CursorStyle}
47  * @see {@link Line}
48  * @see {@link HorizontalLineStyle}
49  * @see {@link VerticalLineStyle}
50  */
51 export interface LineStyle
52   extends SystemStyle, BoxStyle, LayoutItemStyle, Omit<PaintStyle, 'fill'>, CursorStyle {
53   /** Ordered coordinate pairs describing the path vertices. See {@link LineStyle}. */
54   coords: [number, number][];
55   /**
56    * Width used for line hit testing independently of the visible stroke width. See
57    * {@link LineStyle}.
58    */
59   lineInteractionWidth?: number;
60 }
61 
62 type ResolvedLineStyle = ResolvedDrawingStyle<LineStyle>;
63 
64 export function renderLine(
65   props: LineProps,
66   style: Readonly<ResolvedLineStyle>,
67   ctx: RenderingContext2D,
68 ) {
69   const {
70     coords,
71     stroke,
72     strokeWidth,
73     opacity,
74     cursor,
75     lineInteractionWidth,
76   } = style;
77   const linePath = createLinePath({ coords });
78   const interactionWidth = lineInteractionWidth ?? strokeWidth ?? 0;
79 
80   return renderPath(
81     {
82       ...props,
83       keyboardNavigationBounds: props.keyboardNavigationBounds ??
84         lineBounds(coords, interactionWidth),
85     },
86     {
87       d: linePath,
88       stroke,
89       strokeWidth,
90       opacity,
91       cursor,
92       lineInteractionWidth: interactionWidth,
93     },
94     ctx,
95   );
96 }
97 
98 /**
99  * Describes a polyline primitive for JSX or createElement authoring.
100  *
101  * @param props - Authored component inputs, supplied through JSX or createElement. See the linked
102  * props and style types.
103  * @throws When called directly; Pibbl mounts this component through JSX or createElement.
104  *
105  * @see {@link LineProps}
106  * @see {@link LineStyle}
107  */
108 export const Line = definePrimitive<
109   LineProps,
110   LineStyle,
111   LineStyle,
112   ResolvedLineStyle
113 >(renderLine, {
114   resolveStyle: (style, context) => ({
115     ...stripLayoutParticipation(style),
116     coords: validateCoordinates(
117       style.coords,
118       'coords',
119       layoutGeometryErrorFactory(context),
120     ),
121   }) as ResolvedLineStyle,
122 });
123 registerOpaqueStyleProperties(Line, ['stroke', 'cursor']);
124 
125 function lineBounds(
126   coords: readonly [number, number][],
127   interactionWidth: number,
128 ) {
129   if (coords.length === 0) return undefined;
130   const halfWidth = interactionWidth / 2;
131   const xs = coords.map(([x]) => x);
132   const ys = coords.map(([, y]) => y);
133   const left = Math.min(...xs) - halfWidth;
134   const right = Math.max(...xs) + halfWidth;
135   const top = Math.min(...ys) - halfWidth;
136   const bottom = Math.max(...ys) + halfWidth;
137   return {
138     x: left,
139     y: top,
140     width: right - left,
141     height: bottom - top,
142   };
143 }
144 
145 function layoutGeometryErrorFactory(context: Readonly<StyleResolutionContext>) {
146   return (property: string, value: unknown, reason: string) => layoutDiagnostic({
147     component: context.component,
148     property,
149     value,
150     algorithm: context.algorithm,
151     constraints: context.constraints,
152     reason,
153   });
154 }
155 

Documentation built with @pibbl/core 0.0.2, revision 272a94a. ALPHA — NOT FOR PRODUCTION USE.