drawnui-react 0.1.0-preview.1 → 0.1.0-preview.10
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/README.md +87 -29
- package/SKIPPED.md +20 -15
- package/dist/controls/SkiaButton.d.ts +4 -1
- package/dist/controls/SkiaButton.js +20 -7
- package/dist/controls/SkiaCarousel.d.ts +6 -0
- package/dist/controls/SkiaCarousel.js +7 -1
- package/dist/controls/SkiaCheckbox.d.ts +0 -1
- package/dist/controls/SkiaCheckbox.js +0 -6
- package/dist/controls/SkiaEditor.js +8 -0
- package/dist/controls/SkiaLabel.d.ts +2 -0
- package/dist/controls/SkiaLabel.js +9 -0
- package/dist/controls/SkiaLayout.d.ts +10 -0
- package/dist/controls/SkiaLayout.js +64 -1
- package/dist/controls/SkiaRadioButton.js +1 -2
- package/dist/controls/SkiaScroll.d.ts +9 -0
- package/dist/controls/SkiaScroll.js +120 -13
- package/dist/controls/SkiaScrollBar.d.ts +27 -0
- package/dist/controls/SkiaScrollBar.js +51 -2
- package/dist/controls/SkiaShaderCarousel.d.ts +5 -0
- package/dist/controls/SkiaShaderCarousel.js +5 -0
- package/dist/controls/SkiaSwitch.d.ts +0 -1
- package/dist/controls/SkiaSwitch.js +0 -6
- package/dist/controls/SkiaToggle.d.ts +18 -4
- package/dist/controls/SkiaToggle.js +45 -3
- package/dist/core/Canvas.d.ts +82 -4
- package/dist/core/Canvas.js +255 -9
- package/dist/core/Gestures.d.ts +47 -1
- package/dist/core/Gestures.js +43 -1
- package/dist/core/SkiaControl.d.ts +30 -1
- package/dist/core/SkiaControl.js +59 -3
- package/dist/core/Styles.d.ts +51 -0
- package/dist/core/Styles.js +92 -0
- package/dist/core/Super.d.ts +10 -1
- package/dist/core/Super.js +20 -3
- package/dist/core/Types.d.ts +7 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/react/SkiaShell.js +5 -2
- package/dist/react/index.d.ts +22 -4
- package/dist/react/index.js +25 -4
- package/dist/react/reconciler.js +2 -0
- package/dist/vite/index.d.ts +74 -0
- package/dist/vite/index.js +204 -0
- package/package.json +35 -4
- package/PARITY.md +0 -287
|
@@ -3,7 +3,7 @@ import { type Color, type LayoutOptions, SKRect, ScaledSize, type SkiaCacheType,
|
|
|
3
3
|
import { type IOverlayEffect } from "./Animators";
|
|
4
4
|
import { Easing } from "./Easing";
|
|
5
5
|
import type { Canvas } from "./Canvas";
|
|
6
|
-
import { ControlTappedEventArgs, GestureEventProcessingInfo, type LockTouch, SKPoint, SkiaGesturesInfo, SkiaGesturesParameters } from "./Gestures";
|
|
6
|
+
import { ContextMenuEventArgs, ControlTappedEventArgs, GestureEventProcessingInfo, type LockTouch, SKPoint, SkiaGesturesInfo, SkiaGesturesParameters } from "./Gestures";
|
|
7
7
|
import { type CachedTexture, type IPostRendererEffect, type SkiaEffect } from "./SkiaEffect";
|
|
8
8
|
/** Mirrors DrawnUi DrawingContext: ctx.Context.Canvas / Surface, ctx.Destination (pixels), ctx.Scale. */
|
|
9
9
|
export interface DrawingContext {
|
|
@@ -180,11 +180,24 @@ export declare class SkiaControl {
|
|
|
180
180
|
/** Clip overlay effects to the control's shape (CreateClip). */
|
|
181
181
|
ClipEffects: boolean;
|
|
182
182
|
Tapped?: (sender: SkiaControl, e: ControlTappedEventArgs) => void;
|
|
183
|
+
/**
|
|
184
|
+
* Context-menu request over this control (right click, long press on touch, Menu key). Return true to handle it:
|
|
185
|
+
* the browser's own canvas menu is suppressed. Routed deepest child first, then parents, then Canvas.ContextMenu.
|
|
186
|
+
* Not in DrawnUi.Net (a web concept).
|
|
187
|
+
*/
|
|
188
|
+
ContextMenu?: (sender: SkiaControl, e: ContextMenuEventArgs) => boolean | void;
|
|
183
189
|
ChildTapped?: (sender: SkiaControl, e: ControlTappedEventArgs) => void;
|
|
184
190
|
/** Raw gesture hook: set e.Consumed = true to stop propagation (not for Up). */
|
|
185
191
|
ConsumeGestures?: (sender: SkiaControl, e: SkiaGesturesInfo) => void;
|
|
186
192
|
Parent?: SkiaControl;
|
|
187
193
|
/** Containers override; a leaf control cannot host children. */
|
|
194
|
+
private styledInitially;
|
|
195
|
+
/**
|
|
196
|
+
* C# ApplyInitialStyles: takes the property defaults registered with `ConfigureStyles` for this control's class.
|
|
197
|
+
* The reconciler calls it with `atConstruction` right after `new`, before the JSX props, so props win; a control
|
|
198
|
+
* built in code-behind is styled on its first measure and keeps whatever the code already set.
|
|
199
|
+
*/
|
|
200
|
+
ApplyInitialStyles(atConstruction?: boolean): void;
|
|
188
201
|
AddSubView(_control: SkiaControl): void;
|
|
189
202
|
InsertSubView(_index: number, control: SkiaControl): void;
|
|
190
203
|
RemoveSubView(_control: SkiaControl): void;
|
|
@@ -235,6 +248,11 @@ export declare class SkiaControl {
|
|
|
235
248
|
get AccessibilityCanInteract(): boolean;
|
|
236
249
|
set AccessibilityCanInteract(v: boolean);
|
|
237
250
|
protected DefaultAccessibilityCanInteract(): boolean;
|
|
251
|
+
/**
|
|
252
|
+
* Whether the mouse at this point (pixels, relative to DrawingRect's origin) is over something tappable, for the
|
|
253
|
+
* host's pointer cursor. Default: the control as a whole (AccessibilityCanInteract); SkiaLabel adds its tappable spans.
|
|
254
|
+
*/
|
|
255
|
+
WantsPointerCursor(_x: number, _y: number): boolean;
|
|
238
256
|
/** aria-pressed for toggles; undefined = not a toggle. */
|
|
239
257
|
get AccessibilityIsPressed(): boolean | undefined;
|
|
240
258
|
set AccessibilityIsPressed(v: boolean | undefined);
|
|
@@ -364,12 +382,23 @@ export declare class SkiaControl {
|
|
|
364
382
|
* cache holds its composited output, so those are staled before redrawing (DrawnUi RedrawCanvas + parent invalidation).
|
|
365
383
|
*/
|
|
366
384
|
RepaintComposition(): void;
|
|
385
|
+
/**
|
|
386
|
+
* DrawnUi IsParentIndependent: this control's size never changes the layout around it, so its own invalidation
|
|
387
|
+
* stops here (ancestors composite again, nothing above re-measures). The parent re-measures it on draw when
|
|
388
|
+
* NeedMeasure is set. Used by SkiaScroll for its scroll bars.
|
|
389
|
+
*/
|
|
390
|
+
IsParentIndependent: boolean;
|
|
367
391
|
/** Size or content may have changed: this control and all ancestors remeasure and re-record. */
|
|
368
392
|
InvalidateMeasure(): void;
|
|
369
393
|
/** Hit rect in pixels (no transforms in this port, so it is DrawingRect). */
|
|
370
394
|
get HitBoxAuto(): SKRect;
|
|
371
395
|
HitIsInside(x: number, y: number): boolean;
|
|
372
396
|
IsGestureForChild(child: SkiaControl, point: SKPoint): boolean;
|
|
397
|
+
/**
|
|
398
|
+
* Routes a context-menu request like a tap: visible, non-transparent children under the point first (top-most
|
|
399
|
+
* first, each in its own transformed space), then this control's ContextMenu handler. True = handled.
|
|
400
|
+
*/
|
|
401
|
+
ProcessContextMenu(point: SKPoint, e: ContextMenuEventArgs): boolean;
|
|
373
402
|
/** Children that may receive gestures, top-most LAST (layouts return their Views). */
|
|
374
403
|
protected GetGestureListeners(): readonly SkiaControl[];
|
|
375
404
|
private static readonly NoListeners;
|
package/dist/core/SkiaControl.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Super } from "./Super";
|
|
2
|
+
import { SkiaStyles } from "./Styles";
|
|
2
3
|
import { Colors, SKRect, ScaledSize, Thickness } from "./Types";
|
|
3
4
|
import { RippleAnimator, SkiaValueAnimator } from "./Animators";
|
|
4
5
|
import { Easing } from "./Easing";
|
|
@@ -238,12 +239,29 @@ export class SkiaControl {
|
|
|
238
239
|
ClipEffects = true;
|
|
239
240
|
// ---- gesture events (single handler each; C# events map to one callback prop) ----
|
|
240
241
|
Tapped;
|
|
242
|
+
/**
|
|
243
|
+
* Context-menu request over this control (right click, long press on touch, Menu key). Return true to handle it:
|
|
244
|
+
* the browser's own canvas menu is suppressed. Routed deepest child first, then parents, then Canvas.ContextMenu.
|
|
245
|
+
* Not in DrawnUi.Net (a web concept).
|
|
246
|
+
*/
|
|
247
|
+
ContextMenu;
|
|
241
248
|
ChildTapped;
|
|
242
249
|
/** Raw gesture hook: set e.Consumed = true to stop propagation (not for Up). */
|
|
243
250
|
ConsumeGestures;
|
|
244
251
|
// ---- tree ----
|
|
245
252
|
Parent;
|
|
246
253
|
/** Containers override; a leaf control cannot host children. */
|
|
254
|
+
styledInitially = false;
|
|
255
|
+
/**
|
|
256
|
+
* C# ApplyInitialStyles: takes the property defaults registered with `ConfigureStyles` for this control's class.
|
|
257
|
+
* The reconciler calls it with `atConstruction` right after `new`, before the JSX props, so props win; a control
|
|
258
|
+
* built in code-behind is styled on its first measure and keeps whatever the code already set.
|
|
259
|
+
*/
|
|
260
|
+
ApplyInitialStyles(atConstruction = false) {
|
|
261
|
+
this.styledInitially = true;
|
|
262
|
+
if (!SkiaStyles.IsEmpty)
|
|
263
|
+
SkiaStyles.Apply(this, atConstruction);
|
|
264
|
+
}
|
|
247
265
|
AddSubView(_control) { throw new Error(`DrawnUi: ${this.constructor.name} cannot host children`); }
|
|
248
266
|
InsertSubView(_index, control) { this.AddSubView(control); }
|
|
249
267
|
RemoveSubView(_control) { }
|
|
@@ -265,6 +283,8 @@ export class SkiaControl {
|
|
|
265
283
|
* this is what keeps a cached tree from re-measuring text every frame.
|
|
266
284
|
*/
|
|
267
285
|
Measure(widthConstraint, heightConstraint, scale) {
|
|
286
|
+
if (!this.styledInitially)
|
|
287
|
+
this.ApplyInitialStyles(false);
|
|
268
288
|
if (!this.NeedMeasure && this.RenderingScale === scale
|
|
269
289
|
&& Object.is(this.lastWidthConstraint, widthConstraint) && Object.is(this.lastHeightConstraint, heightConstraint)) {
|
|
270
290
|
return this.MeasuredSize;
|
|
@@ -388,6 +408,11 @@ export class SkiaControl {
|
|
|
388
408
|
this.AccessibilityChanged();
|
|
389
409
|
} }
|
|
390
410
|
DefaultAccessibilityCanInteract() { return !!this.Tapped; }
|
|
411
|
+
/**
|
|
412
|
+
* Whether the mouse at this point (pixels, relative to DrawingRect's origin) is over something tappable, for the
|
|
413
|
+
* host's pointer cursor. Default: the control as a whole (AccessibilityCanInteract); SkiaLabel adds its tappable spans.
|
|
414
|
+
*/
|
|
415
|
+
WantsPointerCursor(_x, _y) { return this.AccessibilityCanInteract; }
|
|
391
416
|
/** aria-pressed for toggles; undefined = not a toggle. */
|
|
392
417
|
get AccessibilityIsPressed() { return this.accessibilityIsPressed; }
|
|
393
418
|
set AccessibilityIsPressed(v) { if (this.accessibilityIsPressed !== v) {
|
|
@@ -1057,13 +1082,21 @@ export class SkiaControl {
|
|
|
1057
1082
|
}
|
|
1058
1083
|
this.Repaint();
|
|
1059
1084
|
}
|
|
1085
|
+
/**
|
|
1086
|
+
* DrawnUi IsParentIndependent: this control's size never changes the layout around it, so its own invalidation
|
|
1087
|
+
* stops here (ancestors composite again, nothing above re-measures). The parent re-measures it on draw when
|
|
1088
|
+
* NeedMeasure is set. Used by SkiaScroll for its scroll bars.
|
|
1089
|
+
*/
|
|
1090
|
+
IsParentIndependent = false;
|
|
1060
1091
|
/** Size or content may have changed: this control and all ancestors remeasure and re-record. */
|
|
1061
1092
|
InvalidateMeasure() {
|
|
1062
1093
|
this.NeedMeasure = true;
|
|
1063
1094
|
this.cacheDirty = true;
|
|
1064
1095
|
this.compositeFull = true; // structure may change: a composite cannot patch it
|
|
1065
1096
|
this.effectsMarginCache = undefined;
|
|
1066
|
-
if (this.Parent)
|
|
1097
|
+
if (this.Parent && this.IsParentIndependent)
|
|
1098
|
+
this.RepaintComposition();
|
|
1099
|
+
else if (this.Parent)
|
|
1067
1100
|
this.Parent.InvalidateMeasure();
|
|
1068
1101
|
else
|
|
1069
1102
|
this.Superview?.Update();
|
|
@@ -1079,6 +1112,25 @@ export class SkiaControl {
|
|
|
1079
1112
|
const local = child.TransformPointToLocalSpace(point);
|
|
1080
1113
|
return child.HitIsInside(local.X, local.Y);
|
|
1081
1114
|
}
|
|
1115
|
+
/**
|
|
1116
|
+
* Routes a context-menu request like a tap: visible, non-transparent children under the point first (top-most
|
|
1117
|
+
* first, each in its own transformed space), then this control's ContextMenu handler. True = handled.
|
|
1118
|
+
*/
|
|
1119
|
+
ProcessContextMenu(point, e) {
|
|
1120
|
+
const listeners = this.GetGestureListeners();
|
|
1121
|
+
for (let i = listeners.length - 1; i >= 0; i--) {
|
|
1122
|
+
const listener = listeners[i];
|
|
1123
|
+
if (!listener.IsVisible || listener.InputTransparent || !this.IsGestureForChild(listener, point))
|
|
1124
|
+
continue;
|
|
1125
|
+
if (listener.ProcessContextMenu(listener.TransformPointToLocalSpace(point), e))
|
|
1126
|
+
return true;
|
|
1127
|
+
}
|
|
1128
|
+
if (!this.ContextMenu)
|
|
1129
|
+
return false;
|
|
1130
|
+
e.Control = this;
|
|
1131
|
+
e.Local = new SKPoint(point.X - this.DrawingRect.Left, point.Y - this.DrawingRect.Top);
|
|
1132
|
+
return this.ContextMenu(this, e) === true;
|
|
1133
|
+
}
|
|
1082
1134
|
/** Children that may receive gestures, top-most LAST (layouts return their Views). */
|
|
1083
1135
|
GetGestureListeners() { return SkiaControl.NoListeners; }
|
|
1084
1136
|
static NoListeners = [];
|
|
@@ -1106,15 +1158,19 @@ export class SkiaControl {
|
|
|
1106
1158
|
if (this.ConsumeGestures) {
|
|
1107
1159
|
const sent = new SkiaGesturesInfo(args, apply);
|
|
1108
1160
|
this.ConsumeGestures(this, sent);
|
|
1109
|
-
if (args.Type !== "Up" && sent.Consumed)
|
|
1161
|
+
if (args.Type !== "Up" && sent.Consumed) {
|
|
1162
|
+
args.Event.Handled = true;
|
|
1110
1163
|
return this;
|
|
1164
|
+
}
|
|
1111
1165
|
}
|
|
1112
1166
|
// C# EffectsGestureProcessors: attached effects that process gestures (ISkiaGestureProcessor) see them first
|
|
1113
1167
|
if (!this.DisableEffects)
|
|
1114
1168
|
for (const e of this.visualEffects) {
|
|
1115
1169
|
const p = e.ProcessGestures;
|
|
1116
|
-
if (typeof p === "function" && p.call(e, args, apply) && args.Type !== "Up")
|
|
1170
|
+
if (typeof p === "function" && p.call(e, args, apply) && args.Type !== "Up") {
|
|
1171
|
+
args.Event.Handled = true;
|
|
1117
1172
|
return this;
|
|
1173
|
+
}
|
|
1118
1174
|
}
|
|
1119
1175
|
if (this.CheckChildrenGesturesLocked(args.Type))
|
|
1120
1176
|
return consumedDefault;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { SkiaControl } from "./SkiaControl";
|
|
2
|
+
/**
|
|
3
|
+
* Mirrors DrawnUi.Net `Style` (SharedNet/Internals/Core/Style.cs): a set of property defaults for one control type,
|
|
4
|
+
* registered once at startup through `Super.UseDrawnUi().ConfigureStyles(...)`.
|
|
5
|
+
*
|
|
6
|
+
* The C# `Setters` list of `Setter { Property, Value }` becomes a plain object here, because React controls are
|
|
7
|
+
* classes with plain properties instead of BindableProperty: `Setters: { FontFamily: "FontText", FontSize: 14 }`.
|
|
8
|
+
*/
|
|
9
|
+
export interface Style {
|
|
10
|
+
/** The control class the style targets, e.g. `SkiaLabel`. */
|
|
11
|
+
TargetType: Function;
|
|
12
|
+
/** Also style classes deriving from TargetType (C# Style.ApplyToDerivedTypes). Default false. */
|
|
13
|
+
ApplyToDerivedTypes?: boolean;
|
|
14
|
+
/** Property defaults, by property name. Anything the app sets itself wins over these. */
|
|
15
|
+
Setters: Record<string, unknown>;
|
|
16
|
+
}
|
|
17
|
+
/** Mirrors DrawnUi.Net `IStylesCollection` / `StylesCollection`: the startup registry, static like the C# one. */
|
|
18
|
+
export declare class StylesCollection {
|
|
19
|
+
/** Every style registered through ConfigureStyles, in registration order (C# StylesCollection.Styles). */
|
|
20
|
+
static readonly Styles: Style[];
|
|
21
|
+
AddStyle(style: Style): StylesCollection;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Applies registered styles to controls. Port of the C# `SkiaControl` styles partial
|
|
25
|
+
* (SharedNet/Draw/SkiaControl.NetShared.cs) with one difference forced by the language: C# knows which properties the
|
|
26
|
+
* app set explicitly (BindableProperty + ExplicitPropertiesSet) and skips those; here a property counts as "not set by
|
|
27
|
+
* the app" while it still equals the value the control's own constructor left, captured once per class.
|
|
28
|
+
*/
|
|
29
|
+
export declare class SkiaStyles {
|
|
30
|
+
private static readonly resolved;
|
|
31
|
+
private static readonly defaults;
|
|
32
|
+
/** True while no style is registered: lets Measure skip the whole feature at zero cost. */
|
|
33
|
+
static get IsEmpty(): boolean;
|
|
34
|
+
/** C# InvalidateStylesCache: drop the per-type resolution after the registry changed. */
|
|
35
|
+
static InvalidateStylesCache(): void;
|
|
36
|
+
/**
|
|
37
|
+
* Setters that apply to a control class: the exact-type style plus every ApplyToDerivedTypes style of a base class,
|
|
38
|
+
* furthest base first so a nearer style overrides it, exact type last (C# sorts by inheritance distance).
|
|
39
|
+
*/
|
|
40
|
+
private static For;
|
|
41
|
+
/** The values a freshly constructed control of this class carries, for the properties a style touches. */
|
|
42
|
+
private static SnapshotDefaults;
|
|
43
|
+
/**
|
|
44
|
+
* C# ApplyInitialStyles: applies the registered defaults to one control. `atConstruction` is true when nothing has
|
|
45
|
+
* touched the control yet (the reconciler calls it between `new` and the JSX props), so every setter applies and the
|
|
46
|
+
* pre-style values are kept as this class's defaults; a control styled later only gets the properties that still
|
|
47
|
+
* hold those defaults, which is how an explicit assignment wins — the C# ExplicitPropertiesSet rule without
|
|
48
|
+
* property accessors.
|
|
49
|
+
*/
|
|
50
|
+
static Apply(control: SkiaControl, atConstruction: boolean): void;
|
|
51
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/** Mirrors DrawnUi.Net `IStylesCollection` / `StylesCollection`: the startup registry, static like the C# one. */
|
|
2
|
+
export class StylesCollection {
|
|
3
|
+
/** Every style registered through ConfigureStyles, in registration order (C# StylesCollection.Styles). */
|
|
4
|
+
static Styles = [];
|
|
5
|
+
AddStyle(style) {
|
|
6
|
+
StylesCollection.Styles.push(style);
|
|
7
|
+
SkiaStyles.InvalidateStylesCache();
|
|
8
|
+
return this;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Applies registered styles to controls. Port of the C# `SkiaControl` styles partial
|
|
13
|
+
* (SharedNet/Draw/SkiaControl.NetShared.cs) with one difference forced by the language: C# knows which properties the
|
|
14
|
+
* app set explicitly (BindableProperty + ExplicitPropertiesSet) and skips those; here a property counts as "not set by
|
|
15
|
+
* the app" while it still equals the value the control's own constructor left, captured once per class.
|
|
16
|
+
*/
|
|
17
|
+
export class SkiaStyles {
|
|
18
|
+
static resolved = new Map();
|
|
19
|
+
static defaults = new Map();
|
|
20
|
+
/** True while no style is registered: lets Measure skip the whole feature at zero cost. */
|
|
21
|
+
static get IsEmpty() { return StylesCollection.Styles.length === 0; }
|
|
22
|
+
/** C# InvalidateStylesCache: drop the per-type resolution after the registry changed. */
|
|
23
|
+
static InvalidateStylesCache() { SkiaStyles.resolved.clear(); SkiaStyles.defaults.clear(); }
|
|
24
|
+
/**
|
|
25
|
+
* Setters that apply to a control class: the exact-type style plus every ApplyToDerivedTypes style of a base class,
|
|
26
|
+
* furthest base first so a nearer style overrides it, exact type last (C# sorts by inheritance distance).
|
|
27
|
+
*/
|
|
28
|
+
static For(type) {
|
|
29
|
+
let hit = SkiaStyles.resolved.get(type);
|
|
30
|
+
if (hit)
|
|
31
|
+
return hit;
|
|
32
|
+
const inherited = [];
|
|
33
|
+
let exact;
|
|
34
|
+
for (const style of StylesCollection.Styles) {
|
|
35
|
+
if (style.TargetType === type) {
|
|
36
|
+
exact = style;
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
if (!style.ApplyToDerivedTypes)
|
|
40
|
+
continue;
|
|
41
|
+
let distance = 0;
|
|
42
|
+
for (let base = Object.getPrototypeOf(type); base; base = Object.getPrototypeOf(base)) {
|
|
43
|
+
distance++;
|
|
44
|
+
if (base === style.TargetType) {
|
|
45
|
+
inherited.push({ Distance: distance, Style: style });
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
inherited.sort((a, b) => b.Distance - a.Distance);
|
|
51
|
+
const setters = {};
|
|
52
|
+
for (const i of inherited)
|
|
53
|
+
Object.assign(setters, i.Style.Setters);
|
|
54
|
+
if (exact)
|
|
55
|
+
Object.assign(setters, exact.Setters);
|
|
56
|
+
hit = { Setters: setters, Empty: Object.keys(setters).length === 0 };
|
|
57
|
+
SkiaStyles.resolved.set(type, hit);
|
|
58
|
+
return hit;
|
|
59
|
+
}
|
|
60
|
+
/** The values a freshly constructed control of this class carries, for the properties a style touches. */
|
|
61
|
+
static SnapshotDefaults(type, sample, keys) {
|
|
62
|
+
if (SkiaStyles.defaults.has(type))
|
|
63
|
+
return;
|
|
64
|
+
const known = {};
|
|
65
|
+
for (const key of keys)
|
|
66
|
+
known[key] = sample[key];
|
|
67
|
+
SkiaStyles.defaults.set(type, known);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* C# ApplyInitialStyles: applies the registered defaults to one control. `atConstruction` is true when nothing has
|
|
71
|
+
* touched the control yet (the reconciler calls it between `new` and the JSX props), so every setter applies and the
|
|
72
|
+
* pre-style values are kept as this class's defaults; a control styled later only gets the properties that still
|
|
73
|
+
* hold those defaults, which is how an explicit assignment wins — the C# ExplicitPropertiesSet rule without
|
|
74
|
+
* property accessors.
|
|
75
|
+
*/
|
|
76
|
+
static Apply(control, atConstruction) {
|
|
77
|
+
const type = control.constructor;
|
|
78
|
+
const resolved = SkiaStyles.For(type);
|
|
79
|
+
if (resolved.Empty)
|
|
80
|
+
return;
|
|
81
|
+
const keys = Object.keys(resolved.Setters);
|
|
82
|
+
const target = control;
|
|
83
|
+
if (atConstruction)
|
|
84
|
+
SkiaStyles.SnapshotDefaults(type, control, keys);
|
|
85
|
+
const defaults = atConstruction ? undefined : SkiaStyles.defaults.get(type);
|
|
86
|
+
for (const key of keys) {
|
|
87
|
+
// no snapshot for this class (nothing of it ever went through the reconciler): apply as a plain default
|
|
88
|
+
if (!defaults || Object.is(target[key], defaults[key]))
|
|
89
|
+
target[key] = resolved.Setters[key];
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
package/dist/core/Super.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Thickness } from "./Types";
|
|
2
|
+
import { StylesCollection } from "./Styles";
|
|
2
3
|
import type { CanvasKit, Font, Typeface } from "canvaskit-wasm";
|
|
3
4
|
/** Mirrors DrawnUi.Net IFontCollection: fonts.AddFont(source, alias[, weight]), plus the Blazor-head AddSymbols/AddEmojis. */
|
|
4
5
|
export declare class FontCollection {
|
|
@@ -16,10 +17,16 @@ export declare class FontCollection {
|
|
|
16
17
|
/** Registers a face under an alias; weight 100..900 lets FontWeight / FontAttributes=Bold pick the right file. */
|
|
17
18
|
AddFont(source: string, alias: string, weight?: number): FontCollection;
|
|
18
19
|
}
|
|
19
|
-
/** Mirrors DrawnUi.Net DrawnUiBuilder: Super.UseDrawnUi().ConfigureFonts(...).BuildAsync(). */
|
|
20
|
+
/** Mirrors DrawnUi.Net DrawnUiBuilder: Super.UseDrawnUi().ConfigureFonts(...).ConfigureStyles(...).BuildAsync(). */
|
|
20
21
|
export declare class DrawnUiBuilder {
|
|
21
22
|
private readonly fonts;
|
|
22
23
|
ConfigureFonts(configure: (fonts: FontCollection) => void): DrawnUiBuilder;
|
|
24
|
+
/**
|
|
25
|
+
* Mirrors DrawnUi.Net ConfigureStyles: property defaults per control type, applied to every control unless the app
|
|
26
|
+
* sets that property itself. The usual one, same as the .NET Blazor / Fiddle hosts:
|
|
27
|
+
* `styles.AddStyle({ TargetType: SkiaLabel, ApplyToDerivedTypes: true, Setters: { FontFamily: "FontText" } })`.
|
|
28
|
+
*/
|
|
29
|
+
ConfigureStyles(configure: (styles: StylesCollection) => void): DrawnUiBuilder;
|
|
23
30
|
/** Loads CanvasKit + registered fonts. Must complete before the first Canvas is created. */
|
|
24
31
|
BuildAsync(): Promise<void>;
|
|
25
32
|
}
|
|
@@ -39,6 +46,8 @@ export declare class Super {
|
|
|
39
46
|
static readonly Fonts: Map<string, Map<number, Typeface>>;
|
|
40
47
|
/** First registered font, or CanvasKit's built-in one. */
|
|
41
48
|
static DefaultTypeface?: Typeface;
|
|
49
|
+
/** CanvasKit's built-in face: what an empty FontFamily resolves to, like C# SKTypeface.CreateDefault(). */
|
|
50
|
+
static SkiaDefaultTypeface?: Typeface;
|
|
42
51
|
private static readonly fontCache;
|
|
43
52
|
static UseDrawnUi(): DrawnUiBuilder;
|
|
44
53
|
/** Master switch: false makes every control render uncached (DrawnUi Super.CacheEnabled). */
|
package/dist/core/Super.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Thickness } from "./Types";
|
|
2
|
+
import { StylesCollection } from "./Styles";
|
|
2
3
|
// the "full" build: same API plus Skottie (SkiaLottie) and the paragraph module; +0.9 MB raw over the default build
|
|
3
4
|
import CanvasKitInit from "canvaskit-wasm/bin/full/canvaskit.js";
|
|
4
5
|
import wasmUrl from "canvaskit-wasm/bin/full/canvaskit.wasm?url";
|
|
@@ -22,13 +23,22 @@ export class FontCollection {
|
|
|
22
23
|
return this;
|
|
23
24
|
}
|
|
24
25
|
}
|
|
25
|
-
/** Mirrors DrawnUi.Net DrawnUiBuilder: Super.UseDrawnUi().ConfigureFonts(...).BuildAsync(). */
|
|
26
|
+
/** Mirrors DrawnUi.Net DrawnUiBuilder: Super.UseDrawnUi().ConfigureFonts(...).ConfigureStyles(...).BuildAsync(). */
|
|
26
27
|
export class DrawnUiBuilder {
|
|
27
28
|
fonts = new FontCollection();
|
|
28
29
|
ConfigureFonts(configure) {
|
|
29
30
|
configure(this.fonts);
|
|
30
31
|
return this;
|
|
31
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* Mirrors DrawnUi.Net ConfigureStyles: property defaults per control type, applied to every control unless the app
|
|
35
|
+
* sets that property itself. The usual one, same as the .NET Blazor / Fiddle hosts:
|
|
36
|
+
* `styles.AddStyle({ TargetType: SkiaLabel, ApplyToDerivedTypes: true, Setters: { FontFamily: "FontText" } })`.
|
|
37
|
+
*/
|
|
38
|
+
ConfigureStyles(configure) {
|
|
39
|
+
configure(new StylesCollection());
|
|
40
|
+
return this;
|
|
41
|
+
}
|
|
32
42
|
/** Loads CanvasKit + registered fonts. Must complete before the first Canvas is created. */
|
|
33
43
|
async BuildAsync() {
|
|
34
44
|
Super.CK = await CanvasKitInit({ locateFile: () => wasmUrl });
|
|
@@ -61,7 +71,8 @@ export class DrawnUiBuilder {
|
|
|
61
71
|
if (!Super.DefaultFontAlias)
|
|
62
72
|
Super.DefaultFontAlias = f.Alias;
|
|
63
73
|
}
|
|
64
|
-
Super.
|
|
74
|
+
Super.SkiaDefaultTypeface ??= Super.CK.Typeface.GetDefault() ?? undefined;
|
|
75
|
+
Super.DefaultTypeface ??= Super.SkiaDefaultTypeface;
|
|
65
76
|
}
|
|
66
77
|
}
|
|
67
78
|
/** Mirrors DrawnUi static Super: global engine state. */
|
|
@@ -105,6 +116,8 @@ export class Super {
|
|
|
105
116
|
static Fonts = new Map();
|
|
106
117
|
/** First registered font, or CanvasKit's built-in one. */
|
|
107
118
|
static DefaultTypeface;
|
|
119
|
+
/** CanvasKit's built-in face: what an empty FontFamily resolves to, like C# SKTypeface.CreateDefault(). */
|
|
120
|
+
static SkiaDefaultTypeface;
|
|
108
121
|
static fontCache = new Map();
|
|
109
122
|
static UseDrawnUi() { return new DrawnUiBuilder(); }
|
|
110
123
|
/** Master switch: false makes every control render uncached (DrawnUi Super.CacheEnabled). */
|
|
@@ -145,7 +158,11 @@ export class Super {
|
|
|
145
158
|
}
|
|
146
159
|
/** Nearest registered weight of the alias (empty alias = the first registered family); reports the weight actually used. */
|
|
147
160
|
static ResolveTypeface(alias, weight) {
|
|
148
|
-
|
|
161
|
+
// No family: the Skia built-in face, like C# SkiaFontManager.GetFont("") -> SKTypeface.CreateDefault(). An app that
|
|
162
|
+
// wants its own font everywhere registers it as a style default (ConfigureStyles), exactly as the .NET hosts do.
|
|
163
|
+
if (!alias)
|
|
164
|
+
return { Typeface: Super.SkiaDefaultTypeface ?? Super.DefaultTypeface ?? null, Weight: 400 };
|
|
165
|
+
const weights = Super.Fonts.get(alias);
|
|
149
166
|
const target = weight > 0 ? weight : 400;
|
|
150
167
|
if (weights && weights.size > 0) {
|
|
151
168
|
if (weights.has(target))
|
package/dist/core/Types.d.ts
CHANGED
|
@@ -134,9 +134,14 @@ export type DrawImageAlignment = "Start" | "Center" | "End";
|
|
|
134
134
|
/** MAUI/DrawnUi ScrollOrientation. */
|
|
135
135
|
export type ScrollOrientation = "Vertical" | "Horizontal" | "Both" | "Neither";
|
|
136
136
|
/**
|
|
137
|
-
* DrawnUi SkiaCacheType. Operations = recorded draw commands (SkPicture) replayed each frame
|
|
137
|
+
* DrawnUi SkiaCacheType. Operations / OperationsFull = recorded draw commands (SkPicture) replayed each frame.
|
|
138
138
|
* Image = offscreen surface snapshot blitted each frame (GPU-backed when the canvas is WebGL).
|
|
139
|
-
*
|
|
139
|
+
* ImageDoubleBuffered = an Image cache that keeps the previous one and draws it — or DrawPlaceholder when there is
|
|
140
|
+
* none — while a new one cannot be produced yet.
|
|
141
|
+
* ImageComposite / ImageCompositeGPU = an Image cache whose offscreen surface survives between records: when only
|
|
142
|
+
* children changed, their old and new bounds plus the siblings those overlap are erased and only those children are
|
|
143
|
+
* painted again.
|
|
144
|
+
* GPU resolves to Image and ImageCompositeGPU to ImageComposite; the browser has no separate GPU-only path.
|
|
140
145
|
*/
|
|
141
146
|
export type SkiaCacheType = "None" | "Operations" | "OperationsFull" | "Image" | "ImageDoubleBuffered" | "ImageComposite" | "ImageCompositeGPU" | "GPU";
|
|
142
147
|
/** DrawnUi GradientType (Conical accepted, drawn as Circular). */
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
package/dist/react/SkiaShell.js
CHANGED
|
@@ -440,9 +440,12 @@ export const SkiaShell = forwardRef(function SkiaShell({ Routes, children, NavBa
|
|
|
440
440
|
}
|
|
441
441
|
})();
|
|
442
442
|
}
|
|
443
|
-
else
|
|
444
|
-
// forward
|
|
443
|
+
else {
|
|
444
|
+
// forward (depth grew), or the same depth with another hash = a plain <a href="#/route"> link or a typed hash:
|
|
445
|
+
// the hash names the pages, rebuild the stack without animation (overlays cannot be restored)
|
|
445
446
|
const target = parseHash();
|
|
447
|
+
if (depth === history.current.length && target.join("/") === live.current.stack.join("/"))
|
|
448
|
+
return;
|
|
446
449
|
history.current = target.map(() => "page");
|
|
447
450
|
window.history.replaceState({ [HISTORY_KEY]: history.current.length }, "", hashFor(target));
|
|
448
451
|
setStacks((all) => { const next = all.slice(); next[live.current.selectedTab] = target; return next; });
|
package/dist/react/index.d.ts
CHANGED
|
@@ -31,9 +31,17 @@ import { SkiaScroll as SkiaScrollCtrl } from "../controls/SkiaScroll";
|
|
|
31
31
|
import type { SkiaShape as SkiaShapeCtrl } from "../controls/SkiaShape";
|
|
32
32
|
import type { Color, RenderingModeType } from "../core/Types";
|
|
33
33
|
import type { GesturesMode } from "../core/Gestures";
|
|
34
|
-
/**
|
|
34
|
+
/**
|
|
35
|
+
* Public settable properties of a control become its JSX props, same PascalCase names as C#.
|
|
36
|
+
*
|
|
37
|
+
* Function-typed members split in two: a METHOD (`Update(): void`) is not a prop and is dropped,
|
|
38
|
+
* while an EVENT is declared optional (`Tapped?: (sender, e) => void`) and is exactly the thing a
|
|
39
|
+
* caller passes in JSX. Optionality is the discriminator; without it every handler was missing
|
|
40
|
+
* from the props type, so `<SkiaButton Tapped={…} />` worked at runtime but failed to typecheck
|
|
41
|
+
* and never appeared in completion.
|
|
42
|
+
*/
|
|
35
43
|
type PropsOf<T> = Partial<{
|
|
36
|
-
[K in keyof T as T[K] extends Function ? never : K extends "Children" | "Views" | "Parent" | "Spans" | "GridStructure" | "AccessibilityId" | "IsAccessibilityElement" | "HasTransform" | "RenderTransformMatrix" | "RenderObjectPrevious" | "LastMeasuredIndex" | "ItemsInsertedAtStart" | "UsingControlStyle" | "Track" | "Thumb" | "FrameOn" | "FrameOff" | "ViewCheckOn" | "ViewOn" | "ViewText" | "Ratio" | "StartThumbX" | "EndThumbX" | "SnapPoints" | "CurrentPosition" | "CurrentSnap" | "ContentOffsetBounds" | "InTransition" | "CanAnimate" | "MaxIndex" | "ChildrenTotal" | "IsAtStart" | "IsAtEnd" | "ScrollProgress" | "ScrollAmount" | "TransitionProgress" | "LastIndex" | "ChildrenCount" | "Horizontal" | "Animator" | "Animation" | "IsPlaying" | "PlayWhenAvailable" | "TotalFrames" | "HasEffects" | "IsDisposed" | "Label" | "IsMultiline" | "HasSelection" | "LayoutVersion" | "LinesCount" | "MeasuredLineHeight" | "Focused" | "SpriteSheet" | "FrameWidth" | "FrameHeight" | "DurationMs" | "FrameDurationMs" | "CurrentSprite" | "Rects" | "HasTapHandler" | "HasDecorations" | "LinesCount" | "Superview" | "DrawingRect" | "MeasuredSize" | "RenderingScale" | "NeedMeasure" | "_superview" | "HitBoxAuto" | "TotalDown" | "TotalTapped" | "TouchDown" | "PostAnimators" | "LoadedSource" | "IsLoading" | "DisplayRect" | "AspectScale" | "Content" | "ContentSize" | "ContentOffsetBounds" | "OverscrollDistance" | "OverScrolled" | "IsUserPanning" | "IsUserFocused" | "IsScrolling" | "IsTemplated" | "FirstVisibleIndex" | "LastVisibleIndex" | "DebugString" | "ChildrenFactory" | "ContextIndex" | "RenderObject" | "UsingCacheType" | "TransitionEffect" | "TransitionFromIndex" | "TransitionToIndex" | "EffectPostRenderers" | "CachedImage" | "ImageBitmap" | "ShouldSubmitOnEnter" | "Header" | "Footer" | "RefreshIndicator" | "ScrollBar" | "ScrollBarHorizontal" | "IsRunning" | "VisibleRatio" | "CurrentIndex" | "LastCompositeRecord" | "DirtyChildrenInternal" | "IsRenderingWithComposition" ? never : K]: T[K];
|
|
44
|
+
[K in keyof T as T[K] extends Function ? (undefined extends T[K] ? K : never) : K extends "Children" | "Views" | "Parent" | "Spans" | "GridStructure" | "AccessibilityId" | "IsAccessibilityElement" | "HasTransform" | "RenderTransformMatrix" | "RenderObjectPrevious" | "LastMeasuredIndex" | "ItemsInsertedAtStart" | "UsingControlStyle" | "Track" | "Thumb" | "FrameOn" | "FrameOff" | "ViewCheckOn" | "ViewOn" | "ViewText" | "Ratio" | "StartThumbX" | "EndThumbX" | "SnapPoints" | "CurrentPosition" | "CurrentSnap" | "ContentOffsetBounds" | "InTransition" | "CanAnimate" | "MaxIndex" | "ChildrenTotal" | "IsAtStart" | "IsAtEnd" | "ScrollProgress" | "ScrollAmount" | "TransitionProgress" | "LastIndex" | "ChildrenCount" | "Horizontal" | "Animator" | "Animation" | "IsPlaying" | "PlayWhenAvailable" | "TotalFrames" | "HasEffects" | "IsDisposed" | "Label" | "IsMultiline" | "HasSelection" | "LayoutVersion" | "LinesCount" | "MeasuredLineHeight" | "Focused" | "SpriteSheet" | "FrameWidth" | "FrameHeight" | "DurationMs" | "FrameDurationMs" | "CurrentSprite" | "Rects" | "HasTapHandler" | "HasDecorations" | "LinesCount" | "Superview" | "DrawingRect" | "MeasuredSize" | "RenderingScale" | "NeedMeasure" | "_superview" | "HitBoxAuto" | "TotalDown" | "TotalTapped" | "TouchDown" | "PostAnimators" | "LoadedSource" | "IsLoading" | "DisplayRect" | "AspectScale" | "Content" | "ContentSize" | "ContentOffsetBounds" | "OverscrollDistance" | "OverScrolled" | "IsUserPanning" | "IsUserFocused" | "IsScrolling" | "IsTemplated" | "FirstVisibleIndex" | "LastVisibleIndex" | "DebugString" | "ChildrenFactory" | "ContextIndex" | "RenderObject" | "UsingCacheType" | "TransitionEffect" | "TransitionFromIndex" | "TransitionToIndex" | "EffectPostRenderers" | "CachedImage" | "ImageBitmap" | "ShouldSubmitOnEnter" | "Header" | "Footer" | "RefreshIndicator" | "ScrollBar" | "ScrollBarHorizontal" | "IsRunning" | "VisibleRatio" | "CurrentIndex" | "LastCompositeRecord" | "DirtyChildrenInternal" | "IsRenderingWithComposition" ? never : K]: T[K];
|
|
37
45
|
}>;
|
|
38
46
|
/** `ref` receives the engine control instance (react-reconciler getPublicInstance). */
|
|
39
47
|
type LeafProps<T> = PropsOf<T> & {
|
|
@@ -92,19 +100,29 @@ export declare const SkiaDrawer: FC<LayoutProps<SkiaDrawerCtrl>>;
|
|
|
92
100
|
export interface CanvasProps {
|
|
93
101
|
BackgroundColor?: Color;
|
|
94
102
|
RenderingMode?: RenderingModeType;
|
|
95
|
-
/**
|
|
103
|
+
/**
|
|
104
|
+
* Disabled (default) / Enabled / Lock, like DrawnUi Canvas.Gestures. Enabled shares input with the page like MAUI's
|
|
105
|
+
* Enabled inside a native scroll view: a touch pan along an axis the page can scroll scrolls the page, and the wheel
|
|
106
|
+
* scrolls it unless a control used the wheel. A page that cannot scroll keeps every touch. Lock keeps all input.
|
|
107
|
+
*/
|
|
96
108
|
Gestures?: GesturesMode;
|
|
97
109
|
children?: ReactNode;
|
|
98
110
|
style?: CSSProperties;
|
|
99
111
|
className?: string;
|
|
100
112
|
/** Receives the engine Canvas (FPS, FrameTime, RenderingScale...). */
|
|
101
113
|
ref?: Ref<CanvasView>;
|
|
114
|
+
/** Context-menu request (right click / long press / Menu key) no control handled; return true to suppress the browser menu. */
|
|
115
|
+
ContextMenu?: CanvasView["ContextMenu"];
|
|
116
|
+
/** DrawnView.WillFirstTimeDraw: once, right before the first frame is drawn, with its drawing context. */
|
|
117
|
+
WillFirstTimeDraw?: CanvasView["WillFirstTimeDraw"];
|
|
118
|
+
/** DrawnView.WasDrawn: after every drawn frame. */
|
|
119
|
+
WasDrawn?: CanvasView["WasDrawn"];
|
|
102
120
|
}
|
|
103
121
|
/**
|
|
104
122
|
* Mirrors DrawnUi Canvas: the bridge between the DOM (react-dom) and the drawn tree (DrawnUi reconciler).
|
|
105
123
|
* Requires Super.UseDrawnUi()...BuildAsync() to have completed.
|
|
106
124
|
*/
|
|
107
|
-
export declare function Canvas({ BackgroundColor, RenderingMode, Gestures, children, style, className, ref: viewRef }: CanvasProps): import("react").JSX.Element;
|
|
125
|
+
export declare function Canvas({ BackgroundColor, RenderingMode, Gestures, children, style, className, ref: viewRef, ContextMenu, WillFirstTimeDraw, WasDrawn }: CanvasProps): import("react").JSX.Element;
|
|
108
126
|
export type { SkiaControl };
|
|
109
127
|
export * from "../index";
|
|
110
128
|
export * from "./SkiaShell";
|
package/dist/react/index.js
CHANGED
|
@@ -49,22 +49,40 @@ export const SkiaCarousel = "SkiaCarousel";
|
|
|
49
49
|
/** Carousel whose slide changes are rendered by an SkSL transition (C# SkiaShaderCarousel); slides need UseCache="Image". */
|
|
50
50
|
export const SkiaShaderCarousel = "SkiaShaderCarousel";
|
|
51
51
|
export const SkiaDrawer = "SkiaDrawer";
|
|
52
|
+
/**
|
|
53
|
+
* One-shot, at the first Canvas mount: removes the HTML the `drawnUiStatic` Vite plugin generated for crawlers
|
|
54
|
+
* (`[data-drawnui-static]`, placed after the mount element). A querySelectorAll once per page, never again; an
|
|
55
|
+
* app built without the plugin finds nothing.
|
|
56
|
+
*/
|
|
57
|
+
let staticRemoved = false;
|
|
58
|
+
function removeStaticContent() {
|
|
59
|
+
if (staticRemoved || typeof document === "undefined")
|
|
60
|
+
return;
|
|
61
|
+
staticRemoved = true;
|
|
62
|
+
for (const el of document.querySelectorAll("[data-drawnui-static]"))
|
|
63
|
+
el.remove();
|
|
64
|
+
}
|
|
52
65
|
/**
|
|
53
66
|
* Mirrors DrawnUi Canvas: the bridge between the DOM (react-dom) and the drawn tree (DrawnUi reconciler).
|
|
54
67
|
* Requires Super.UseDrawnUi()...BuildAsync() to have completed.
|
|
55
68
|
*/
|
|
56
|
-
export function Canvas({ BackgroundColor, RenderingMode, Gestures, children, style, className, ref: viewRef }) {
|
|
69
|
+
export function Canvas({ BackgroundColor, RenderingMode, Gestures, children, style, className, ref: viewRef, ContextMenu, WillFirstTimeDraw, WasDrawn }) {
|
|
57
70
|
const ref = useRef(null);
|
|
58
71
|
const view = useRef(null);
|
|
59
72
|
const root = useRef(null);
|
|
60
73
|
const [engine, setEngine] = useState(null);
|
|
61
74
|
useLayoutEffect(() => {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
75
|
+
// the engine draws frame 1 inside its constructor: the draw events and the rendering mode must be on it before that
|
|
76
|
+
const v = new CanvasView(ref.current, (c) => {
|
|
77
|
+
if (RenderingMode)
|
|
78
|
+
c.RenderingMode = RenderingMode;
|
|
79
|
+
c.WillFirstTimeDraw = WillFirstTimeDraw;
|
|
80
|
+
c.WasDrawn = WasDrawn;
|
|
81
|
+
});
|
|
65
82
|
view.current = v;
|
|
66
83
|
root.current = createDrawnRoot(v);
|
|
67
84
|
setEngine(v);
|
|
85
|
+
removeStaticContent(); // the constructor drew frame 1: the crawlable block (drawnUiStatic) is duplicate content from here on
|
|
68
86
|
return () => { root.current?.unmount(); v.Dispose(); view.current = null; root.current = null; setEngine(null); };
|
|
69
87
|
// RenderingMode is read once at surface creation, like DrawnUi.
|
|
70
88
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
@@ -78,6 +96,9 @@ export function Canvas({ BackgroundColor, RenderingMode, Gestures, children, sty
|
|
|
78
96
|
v.Update();
|
|
79
97
|
}
|
|
80
98
|
v.Gestures = Gestures ?? "Disabled";
|
|
99
|
+
v.ContextMenu = ContextMenu;
|
|
100
|
+
v.WillFirstTimeDraw = WillFirstTimeDraw;
|
|
101
|
+
v.WasDrawn = WasDrawn;
|
|
81
102
|
root.current.render(children);
|
|
82
103
|
});
|
|
83
104
|
// The drawn surface is aria-hidden; the overlay is the accessibility tree (DrawnUi.Blazor pattern).
|
package/dist/react/reconciler.js
CHANGED
|
@@ -85,6 +85,8 @@ const hostConfig = {
|
|
|
85
85
|
if (!ctor)
|
|
86
86
|
throw new Error(`DrawnUi: unknown control <${type}>`);
|
|
87
87
|
const inst = new ctor();
|
|
88
|
+
// ConfigureStyles defaults first, the JSX props below override them (a TextSpan is not a control, it has none)
|
|
89
|
+
inst.ApplyInitialStyles?.(true);
|
|
88
90
|
applyProps(inst, null, props);
|
|
89
91
|
return inst;
|
|
90
92
|
},
|