@fusion.dev/helix-core 0.105.0 → 0.106.0

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.
@@ -3625,6 +3625,50 @@
3625
3625
  },
3626
3626
  "helix": {}
3627
3627
  },
3628
+ {
3629
+ "kind": "class",
3630
+ "description": "The user asked the map to rotate, from a rotate button or key. Turn your map\nin response. Not cancelable, because the frame moves nothing itself.",
3631
+ "name": "HelixMapRotateEvent",
3632
+ "members": [
3633
+ {
3634
+ "kind": "field",
3635
+ "name": "eventName",
3636
+ "type": {
3637
+ "text": "string"
3638
+ },
3639
+ "static": true,
3640
+ "readonly": true,
3641
+ "default": "'helix-map-rotate'"
3642
+ }
3643
+ ],
3644
+ "superclass": {
3645
+ "name": "CustomEvent",
3646
+ "module": "src/events/events.ts"
3647
+ },
3648
+ "helix": {}
3649
+ },
3650
+ {
3651
+ "kind": "class",
3652
+ "description": "The user asked the map to tilt, from a tilt button or key. Tilt your map in\nresponse. Not cancelable, because the frame moves nothing itself.",
3653
+ "name": "HelixMapTiltEvent",
3654
+ "members": [
3655
+ {
3656
+ "kind": "field",
3657
+ "name": "eventName",
3658
+ "type": {
3659
+ "text": "string"
3660
+ },
3661
+ "static": true,
3662
+ "readonly": true,
3663
+ "default": "'helix-map-tilt'"
3664
+ }
3665
+ ],
3666
+ "superclass": {
3667
+ "name": "CustomEvent",
3668
+ "module": "src/events/events.ts"
3669
+ },
3670
+ "helix": {}
3671
+ },
3628
3672
  {
3629
3673
  "kind": "class",
3630
3674
  "description": "A marker, a position, a route or an area changed what it says: where it is,\nits points, its progress, its name.\n\n**Said rather than reflected.** The connector and the frame follow the\nparts, and heard them through their attributes until the parts reflected\nnothing CSS selects on: a live position's coordinates are state, and a\nroute's points are a payload, neither of which belongs in the markup. It\nbubbles to the frame, whose light-DOM children the parts are, and goes no\nfurther, being the family's own bookkeeping rather than news for a page.",
@@ -4422,6 +4466,22 @@
4422
4466
  "module": "src/events/events.ts"
4423
4467
  }
4424
4468
  },
4469
+ {
4470
+ "kind": "js",
4471
+ "name": "HelixMapRotateEvent",
4472
+ "declaration": {
4473
+ "name": "HelixMapRotateEvent",
4474
+ "module": "src/events/events.ts"
4475
+ }
4476
+ },
4477
+ {
4478
+ "kind": "js",
4479
+ "name": "HelixMapTiltEvent",
4480
+ "declaration": {
4481
+ "name": "HelixMapTiltEvent",
4482
+ "module": "src/events/events.ts"
4483
+ }
4484
+ },
4425
4485
  {
4426
4486
  "kind": "js",
4427
4487
  "name": "HelixMapPartChangeEvent",
@@ -4654,7 +4714,7 @@
4654
4714
  "declarations": [
4655
4715
  {
4656
4716
  "kind": "class",
4657
- "description": "Owns only what is universal: normalizing `box-sizing` inside the shadow root,\nso component CSS can rely on border-box on both sides of the boundary.\n\nThere is deliberately no `emit(name, detail)` helper. A dispatch through a\ngeneric helper cannot be named by the manifest analyzer, which recorded the\nparameter identifier and produced a phantom event on every element that\ninherited it. Components dispatch concrete event classes from `events.ts`,\nwhich the analyzer and TypeScript can both see.",
4717
+ "description": "Handles only what every component needs: it sets `box-sizing` inside the\nshadow root, so component CSS can rely on border-box on both sides of the\nboundary.\n\nThere is no `emit(name, detail)` helper. The manifest analyzer cannot name an\nevent dispatched through a generic helper, so components dispatch the\nconcrete event classes from `events.ts`, which the analyzer and TypeScript\ncan both read.",
4658
4718
  "name": "HelixElement",
4659
4719
  "members": [
4660
4720
  {
@@ -5129,7 +5189,7 @@
5129
5189
  "declarations": [
5130
5190
  {
5131
5191
  "kind": "class",
5132
- "description": "This has to be a class rather than a mixin or controller, and that is not a\nstyle preference: the browser reads `formAssociated` off the constructor\nduring element upgrade, which cannot be delegated to a composed object.\n\n`internals` comes from `HelixElement`, which attaches it for every component.\n`attachInternals()` throws on a second call, so there is exactly one owner.\n\nDeliberately small. It carries what every form participant needs and nothing\nmore; `value`, validity, and the reset and restore callbacks land when the\nfirst input does, rather than being designed against components that do not\nexist yet.",
5192
+ "description": "`HelixFormElement` is a class because the browser reads `formAssociated` from\nthe constructor during element upgrade, and a mixin or controller cannot\nsupply it.\n\n`internals` comes from `HelixElement`, which attaches it for every component.\n`attachInternals()` throws on a second call, so only one class attaches it.\n\nThe class carries only what every form participant needs. `value`, validity,\nand the reset and restore callbacks are added with the first input component.",
5133
5193
  "name": "HelixFormElement",
5134
5194
  "members": [
5135
5195
  {
@@ -6436,6 +6496,22 @@
6436
6496
  "module": "./events/events.js"
6437
6497
  }
6438
6498
  },
6499
+ {
6500
+ "kind": "js",
6501
+ "name": "HelixMapRotateEvent",
6502
+ "declaration": {
6503
+ "name": "HelixMapRotateEvent",
6504
+ "module": "./events/events.js"
6505
+ }
6506
+ },
6507
+ {
6508
+ "kind": "js",
6509
+ "name": "HelixMapTiltEvent",
6510
+ "declaration": {
6511
+ "name": "HelixMapTiltEvent",
6512
+ "module": "./events/events.js"
6513
+ }
6514
+ },
6439
6515
  {
6440
6516
  "kind": "js",
6441
6517
  "name": "HelixMapZoomEvent",
@@ -6612,6 +6688,22 @@
6612
6688
  "module": "./events/events.js"
6613
6689
  }
6614
6690
  },
6691
+ {
6692
+ "kind": "js",
6693
+ "name": "MapRotateDetail",
6694
+ "declaration": {
6695
+ "name": "MapRotateDetail",
6696
+ "module": "./events/events.js"
6697
+ }
6698
+ },
6699
+ {
6700
+ "kind": "js",
6701
+ "name": "MapTiltDetail",
6702
+ "declaration": {
6703
+ "name": "MapTiltDetail",
6704
+ "module": "./events/events.js"
6705
+ }
6706
+ },
6615
6707
  {
6616
6708
  "kind": "js",
6617
6709
  "name": "MapZoomDetail",
@@ -11136,7 +11228,7 @@
11136
11228
  "type": {
11137
11229
  "text": "string | undefined"
11138
11230
  },
11139
- "description": "Which controls to show, as a list of `pan`, `zoom`, and `fullscreen`, or `none`."
11231
+ "description": "Which controls to show, as a list such as `pan zoom fullscreen`, or `none`. A family adds words of its own."
11140
11232
  },
11141
11233
  {
11142
11234
  "kind": "field",
@@ -11397,6 +11489,93 @@
11397
11489
  "privacy": "protected",
11398
11490
  "description": "Which controls the surface draws while `controls` is absent.",
11399
11491
  "readonly": true
11492
+ },
11493
+ {
11494
+ "kind": "method",
11495
+ "name": "drawsControls",
11496
+ "privacy": "protected",
11497
+ "return": {
11498
+ "type": {
11499
+ "text": "boolean"
11500
+ }
11501
+ },
11502
+ "parameters": [
11503
+ {
11504
+ "name": "group",
11505
+ "type": {
11506
+ "text": "string"
11507
+ }
11508
+ }
11509
+ ],
11510
+ "description": "Whether `controls`, or the resting controls, names a group."
11511
+ },
11512
+ {
11513
+ "kind": "method",
11514
+ "name": "renderMoreControls",
11515
+ "privacy": "protected",
11516
+ "return": {
11517
+ "type": {
11518
+ "text": "TemplateResult | typeof nothing"
11519
+ }
11520
+ },
11521
+ "description": "More buttons for the group of controls, after zoom, for a family that adds its own."
11522
+ },
11523
+ {
11524
+ "kind": "method",
11525
+ "name": "handleMoreKeys",
11526
+ "privacy": "protected",
11527
+ "return": {
11528
+ "type": {
11529
+ "text": "boolean"
11530
+ }
11531
+ },
11532
+ "parameters": [
11533
+ {
11534
+ "name": "event",
11535
+ "type": {
11536
+ "text": "KeyboardEvent"
11537
+ }
11538
+ }
11539
+ ],
11540
+ "description": "Answers a key the family adds, and says whether it did."
11541
+ },
11542
+ {
11543
+ "kind": "method",
11544
+ "name": "panLabel",
11545
+ "privacy": "protected",
11546
+ "return": {
11547
+ "type": {
11548
+ "text": "string"
11549
+ }
11550
+ },
11551
+ "parameters": [
11552
+ {
11553
+ "name": "way",
11554
+ "type": {
11555
+ "text": "ViewportWay"
11556
+ }
11557
+ }
11558
+ ],
11559
+ "description": "What one pan control is called, for a family that draws its own."
11560
+ },
11561
+ {
11562
+ "kind": "method",
11563
+ "name": "panGlyph",
11564
+ "privacy": "protected",
11565
+ "return": {
11566
+ "type": {
11567
+ "text": "string"
11568
+ }
11569
+ },
11570
+ "parameters": [
11571
+ {
11572
+ "name": "way",
11573
+ "type": {
11574
+ "text": "ViewportWay"
11575
+ }
11576
+ }
11577
+ ],
11578
+ "description": "The arrow drawn on one pan control, for a family that draws its own."
11400
11579
  }
11401
11580
  ],
11402
11581
  "helix": {}
@@ -11412,7 +11591,7 @@
11412
11591
  "type": {
11413
11592
  "text": "string | undefined"
11414
11593
  },
11415
- "description": "Which controls to show, as a list of `pan`, `zoom`, and `fullscreen`, or `none`.\n\n**A list rather than a switch, because the overlap is not symmetric.** A\nsurface a consumer brings often has zoom controls of its own and almost\nnever has pan buttons, so a single switch would make them choose between\na duplicate and nothing.\n\n`none` draws none of them, which is the right answer where what was\nslotted already has controls a reader can find: what this argues for is\nthat *a* findable control exists, never that it is ours. The methods stay\npublic either way, so a consumer drawing their own chrome can still drive\nthe surface.\n\nAbsent is whatever the family rests at, which is this library's rule that\na resting value is an attribute's absence: all three for a map, whose\nreader may have no other way to move it, and zoom and fullscreen for a\nboard, which a reader drags and whose pad took a third of the frame. The\nstylesheet lays the cluster out by which buttons are in it rather than by\nthis attribute, so the two defaults need no rule each.",
11594
+ "description": "Which controls to show, as a list such as `pan zoom fullscreen`, or\n`none`. A family can add words of its own.\n\n**A list rather than a switch, because the overlap is not symmetric.** A\nsurface a consumer brings often has zoom controls of its own and almost\nnever has pan buttons, so a single switch would make them choose between\na duplicate and nothing.\n\n`none` draws none of them, which is the right answer where what was\nslotted already has controls a reader can find: what this argues for is\nthat *a* findable control exists, never that it is ours. The methods stay\npublic either way, so a consumer drawing their own chrome can still drive\nthe surface.\n\nAbsent is whatever the family rests at, which is this library's rule that\na resting value is an attribute's absence: zoom, the compass and\nfullscreen for a map, whose pan buttons a page asks for, and zoom and\nfullscreen for a board, which a reader drags and whose pad took a third\nof the frame. The\nstylesheet lays the cluster out by which buttons are in it rather than by\nthis attribute, so the two defaults need no rule each.",
11416
11595
  "attribute": "controls",
11417
11596
  "reflects": true
11418
11597
  },
@@ -11665,8 +11844,8 @@
11665
11844
  },
11666
11845
  {
11667
11846
  "kind": "method",
11668
- "name": "draws",
11669
- "privacy": "private",
11847
+ "name": "drawsControls",
11848
+ "privacy": "protected",
11670
11849
  "return": {
11671
11850
  "type": {
11672
11851
  "text": "boolean"
@@ -11676,11 +11855,41 @@
11676
11855
  {
11677
11856
  "name": "group",
11678
11857
  "type": {
11679
- "text": "'pan' | 'zoom' | 'fullscreen'"
11858
+ "text": "string"
11859
+ }
11860
+ }
11861
+ ],
11862
+ "description": "Whether `controls`, or the resting controls, names a group."
11863
+ },
11864
+ {
11865
+ "kind": "method",
11866
+ "name": "renderMoreControls",
11867
+ "privacy": "protected",
11868
+ "return": {
11869
+ "type": {
11870
+ "text": "TemplateResult | typeof nothing"
11871
+ }
11872
+ },
11873
+ "description": "More buttons for the group of controls, after zoom. None here; a family adds its own."
11874
+ },
11875
+ {
11876
+ "kind": "method",
11877
+ "name": "handleMoreKeys",
11878
+ "privacy": "protected",
11879
+ "return": {
11880
+ "type": {
11881
+ "text": "boolean"
11882
+ }
11883
+ },
11884
+ "parameters": [
11885
+ {
11886
+ "name": "_event",
11887
+ "type": {
11888
+ "text": "KeyboardEvent"
11680
11889
  }
11681
11890
  }
11682
11891
  ],
11683
- "description": "Whether a named group of controls is one this surface draws."
11892
+ "description": "Answers a key the family adds, and says whether it did. None here."
11684
11893
  },
11685
11894
  {
11686
11895
  "kind": "method",
@@ -11713,8 +11922,8 @@
11713
11922
  },
11714
11923
  {
11715
11924
  "kind": "method",
11716
- "name": "named",
11717
- "privacy": "private",
11925
+ "name": "panLabel",
11926
+ "privacy": "protected",
11718
11927
  "return": {
11719
11928
  "type": {
11720
11929
  "text": "string"
@@ -11732,8 +11941,8 @@
11732
11941
  },
11733
11942
  {
11734
11943
  "kind": "method",
11735
- "name": "glyph",
11736
- "privacy": "private",
11944
+ "name": "panGlyph",
11945
+ "privacy": "protected",
11737
11946
  "return": {
11738
11947
  "type": {
11739
11948
  "text": "string"
@@ -11784,7 +11993,7 @@
11784
11993
  "type": {
11785
11994
  "text": "string | undefined"
11786
11995
  },
11787
- "description": "Which controls to show, as a list of `pan`, `zoom`, and `fullscreen`, or `none`.\n\n**A list rather than a switch, because the overlap is not symmetric.** A\nsurface a consumer brings often has zoom controls of its own and almost\nnever has pan buttons, so a single switch would make them choose between\na duplicate and nothing.\n\n`none` draws none of them, which is the right answer where what was\nslotted already has controls a reader can find: what this argues for is\nthat *a* findable control exists, never that it is ours. The methods stay\npublic either way, so a consumer drawing their own chrome can still drive\nthe surface.\n\nAbsent is whatever the family rests at, which is this library's rule that\na resting value is an attribute's absence: all three for a map, whose\nreader may have no other way to move it, and zoom and fullscreen for a\nboard, which a reader drags and whose pad took a third of the frame. The\nstylesheet lays the cluster out by which buttons are in it rather than by\nthis attribute, so the two defaults need no rule each.",
11996
+ "description": "Which controls to show, as a list such as `pan zoom fullscreen`, or\n`none`. A family can add words of its own.\n\n**A list rather than a switch, because the overlap is not symmetric.** A\nsurface a consumer brings often has zoom controls of its own and almost\nnever has pan buttons, so a single switch would make them choose between\na duplicate and nothing.\n\n`none` draws none of them, which is the right answer where what was\nslotted already has controls a reader can find: what this argues for is\nthat *a* findable control exists, never that it is ours. The methods stay\npublic either way, so a consumer drawing their own chrome can still drive\nthe surface.\n\nAbsent is whatever the family rests at, which is this library's rule that\na resting value is an attribute's absence: zoom, the compass and\nfullscreen for a map, whose pan buttons a page asks for, and zoom and\nfullscreen for a board, which a reader drags and whose pad took a third\nof the frame. The\nstylesheet lays the cluster out by which buttons are in it rather than by\nthis attribute, so the two defaults need no rule each.",
11788
11997
  "fieldName": "controls"
11789
11998
  },
11790
11999
  {
@@ -558,14 +558,45 @@ export declare class HelixMapZoomEvent extends CustomEvent<ViewportZoomDetail> {
558
558
  static readonly eventName = "helix-map-zoom";
559
559
  constructor(detail: MapZoomDetail);
560
560
  }
561
+ /** A rotate request: turn by some degrees, or turn to a bearing. */
562
+ export interface MapRotateDetail {
563
+ /** Degrees to turn the map clockwise. Negative turns it counterclockwise. */
564
+ by?: number;
565
+ /** The bearing to turn the map to, in degrees clockwise from north. `0` faces north. */
566
+ to?: number;
567
+ }
568
+ /** A tilt request, in degrees. */
569
+ export interface MapTiltDetail {
570
+ /** Degrees to tilt the map toward the horizon. Negative tilts it back toward flat. */
571
+ by?: number;
572
+ /** The tilt to go to, in degrees from looking straight down. `0` is flat. */
573
+ to?: number;
574
+ }
575
+ /**
576
+ * The user asked the map to rotate, from a rotate button or key. Turn your map
577
+ * in response. Not cancelable, because the frame moves nothing itself.
578
+ */
579
+ export declare class HelixMapRotateEvent extends CustomEvent<MapRotateDetail> {
580
+ static readonly eventName = "helix-map-rotate";
581
+ constructor(detail: MapRotateDetail);
582
+ }
583
+ /**
584
+ * The user asked the map to tilt, from a tilt button or key. Tilt your map in
585
+ * response. Not cancelable, because the frame moves nothing itself.
586
+ */
587
+ export declare class HelixMapTiltEvent extends CustomEvent<MapTiltDetail> {
588
+ static readonly eventName = "helix-map-tilt";
589
+ constructor(detail: MapTiltDetail);
590
+ }
561
591
  /** What changed on a part of a map: a line or a shape, or something standing on a place. */
562
592
  export interface MapPartChangeDetail {
563
593
  /**
564
594
  * `layer` for a route or an area, which is redrawn; `place` for a marker or
565
595
  * a position, which is moved; `cluster` for a marker gathered into another
566
- * or let go, which moves the frame's tab stop.
596
+ * or let go, which moves the frame's tab stop; `menu` for the frame's layers
597
+ * menu opening, which asks for its map-type previews.
567
598
  */
568
- kind: 'cluster' | 'layer' | 'place';
599
+ kind: 'cluster' | 'layer' | 'menu' | 'place';
569
600
  }
570
601
  /**
571
602
  * A marker, a position, a route or an area changed what it says: where it is,
@@ -1087,6 +1118,8 @@ declare const registry: {
1087
1118
  readonly HelixMapClusterEvent: typeof HelixMapClusterEvent;
1088
1119
  readonly HelixMapPanEvent: typeof HelixMapPanEvent;
1089
1120
  readonly HelixMapPartChangeEvent: typeof HelixMapPartChangeEvent;
1121
+ readonly HelixMapRotateEvent: typeof HelixMapRotateEvent;
1122
+ readonly HelixMapTiltEvent: typeof HelixMapTiltEvent;
1090
1123
  readonly HelixMapZoomEvent: typeof HelixMapZoomEvent;
1091
1124
  readonly HelixMoveEvent: typeof HelixMoveEvent;
1092
1125
  readonly HelixNavigateEvent: typeof HelixNavigateEvent;
@@ -252,6 +252,28 @@ export class HelixMapZoomEvent extends CustomEvent {
252
252
  });
253
253
  }
254
254
  }
255
+ export class HelixMapRotateEvent extends CustomEvent {
256
+ static { this.eventName = 'helix-map-rotate'; }
257
+ constructor(detail) {
258
+ super(HelixMapRotateEvent.eventName, {
259
+ bubbles: true,
260
+ cancelable: false,
261
+ composed: true,
262
+ detail,
263
+ });
264
+ }
265
+ }
266
+ export class HelixMapTiltEvent extends CustomEvent {
267
+ static { this.eventName = 'helix-map-tilt'; }
268
+ constructor(detail) {
269
+ super(HelixMapTiltEvent.eventName, {
270
+ bubbles: true,
271
+ cancelable: false,
272
+ composed: true,
273
+ detail,
274
+ });
275
+ }
276
+ }
255
277
  export class HelixMapPartChangeEvent extends CustomEvent {
256
278
  static { this.eventName = 'helix-map-part-change'; }
257
279
  constructor(detail) {
@@ -581,6 +603,8 @@ const registry = {
581
603
  HelixMapClusterEvent,
582
604
  HelixMapPanEvent,
583
605
  HelixMapPartChangeEvent,
606
+ HelixMapRotateEvent,
607
+ HelixMapTiltEvent,
584
608
  HelixMapZoomEvent,
585
609
  HelixMoveEvent,
586
610
  HelixNavigateEvent,
@@ -4,14 +4,14 @@ import { type StaticValue } from 'lit/static-html.js';
4
4
  * @summary Base class every Helix element extends.
5
5
  *
6
6
  * @description
7
- * Owns only what is universal: normalizing `box-sizing` inside the shadow root,
8
- * so component CSS can rely on border-box on both sides of the boundary.
7
+ * Handles only what every component needs: it sets `box-sizing` inside the
8
+ * shadow root, so component CSS can rely on border-box on both sides of the
9
+ * boundary.
9
10
  *
10
- * There is deliberately no `emit(name, detail)` helper. A dispatch through a
11
- * generic helper cannot be named by the manifest analyzer, which recorded the
12
- * parameter identifier and produced a phantom event on every element that
13
- * inherited it. Components dispatch concrete event classes from `events.ts`,
14
- * which the analyzer and TypeScript can both see.
11
+ * There is no `emit(name, detail)` helper. The manifest analyzer cannot name an
12
+ * event dispatched through a generic helper, so components dispatch the
13
+ * concrete event classes from `events.ts`, which the analyzer and TypeScript
14
+ * can both read.
15
15
  *
16
16
  * @since 0.1.0
17
17
  * @status experimental
@@ -4,17 +4,15 @@ import { HelixElement } from '../helix-element/helix-element.js';
4
4
  * @summary Base for every element that participates in a form.
5
5
  *
6
6
  * @description
7
- * This has to be a class rather than a mixin or controller, and that is not a
8
- * style preference: the browser reads `formAssociated` off the constructor
9
- * during element upgrade, which cannot be delegated to a composed object.
7
+ * `HelixFormElement` is a class because the browser reads `formAssociated` from
8
+ * the constructor during element upgrade, and a mixin or controller cannot
9
+ * supply it.
10
10
  *
11
11
  * `internals` comes from `HelixElement`, which attaches it for every component.
12
- * `attachInternals()` throws on a second call, so there is exactly one owner.
12
+ * `attachInternals()` throws on a second call, so only one class attaches it.
13
13
  *
14
- * Deliberately small. It carries what every form participant needs and nothing
15
- * more; `value`, validity, and the reset and restore callbacks land when the
16
- * first input does, rather than being designed against components that do not
17
- * exist yet.
14
+ * The class carries only what every form participant needs. `value`, validity,
15
+ * and the reset and restore callbacks are added with the first input component.
18
16
  *
19
17
  * @since 0.1.0
20
18
  * @status experimental
package/dist/index.d.ts CHANGED
@@ -20,7 +20,7 @@ export { SourceController, type SourceHost, } from './controllers/source-control
20
20
  export { type TokenColor, TokenColorController, } from './controllers/token-color-controller/token-color-controller.js';
21
21
  export { type Rendered, VirtualizeController, type VirtualizeOptions, type Window as VirtualizeWindow, } from './controllers/virtualize-controller/virtualize-controller.js';
22
22
  export { eventsForTag, registerElementEvents } from './events/element-events.js';
23
- export { type CanvasView, type CaretDetail, type ChangeDetail, type CompleteDetail, type ConnectDetail, type ConsoleDetail, type CopyDetail, type CreateDetail, type DismissDetail, type FacetDetail, type FilterDetail, HelixActivateEvent, HelixBeforeActivateEvent, HelixBeforeChangeEvent, HelixBeforeCloseEvent, HelixBeforeCollapseEvent, HelixBeforeEditEvent, HelixBeforeMoveEvent, HelixBeforeOpenEvent, HelixBeforePanelResizeEvent, HelixBeforePinEvent, HelixBeforeReconnectEvent, HelixBeforeRelabelEvent, HelixBeforeResizeEvent, HelixBeforeSplitEvent, HelixBeforeToggleEvent, HelixCanvasEditEvent, HelixCanvasPanEvent, HelixCanvasViewEvent, HelixCanvasZoomEvent, HelixCaretEvent, HelixCompleteEvent, HelixConnectEvent, HelixConsoleEvent, HelixCopyEvent, HelixCreateEvent, HelixDismissEvent, HelixFacetEvent, HelixFilterEvent, HelixHistoryEvent, HelixMapClusterEvent, HelixMapPanEvent, HelixMapPartChangeEvent, HelixMapZoomEvent, HelixMoveEvent, HelixNavigateEvent, HelixNextEvent, HelixPreviousEvent, HelixReadEvent, HelixReconnectEvent, HelixRelabelEvent, HelixRemoveEvent, HelixReorderEvent, HelixResizeEvent, HelixSampleEndEvent, HelixSampleStartEvent, HelixScrolledEvent, HelixSelectEvent, HelixSendEvent, HelixShortcutEvent, HelixSortEvent, HelixTourAskEvent, type MapClusterDetail, type MapPanDetail, type MapPartChangeDetail, type MapZoomDetail, type MoveDetail, type NavigateDetail, type OpenDetail, type ReadDetail, type ReconnectDetail, type RelabelDetail, type RemoveDetail, type ResizeDetail, type ScrolledDetail, type SelectDetail, type SendDetail, type ShortcutDetail, type SkipDetail, type SortDetail, type ToggleDetail, type ViewportPanDetail, type ViewportZoomDetail, } from './events/events.js';
23
+ export { type CanvasView, type CaretDetail, type ChangeDetail, type CompleteDetail, type ConnectDetail, type ConsoleDetail, type CopyDetail, type CreateDetail, type DismissDetail, type FacetDetail, type FilterDetail, HelixActivateEvent, HelixBeforeActivateEvent, HelixBeforeChangeEvent, HelixBeforeCloseEvent, HelixBeforeCollapseEvent, HelixBeforeEditEvent, HelixBeforeMoveEvent, HelixBeforeOpenEvent, HelixBeforePanelResizeEvent, HelixBeforePinEvent, HelixBeforeReconnectEvent, HelixBeforeRelabelEvent, HelixBeforeResizeEvent, HelixBeforeSplitEvent, HelixBeforeToggleEvent, HelixCanvasEditEvent, HelixCanvasPanEvent, HelixCanvasViewEvent, HelixCanvasZoomEvent, HelixCaretEvent, HelixCompleteEvent, HelixConnectEvent, HelixConsoleEvent, HelixCopyEvent, HelixCreateEvent, HelixDismissEvent, HelixFacetEvent, HelixFilterEvent, HelixHistoryEvent, HelixMapClusterEvent, HelixMapPanEvent, HelixMapPartChangeEvent, HelixMapRotateEvent, HelixMapTiltEvent, HelixMapZoomEvent, HelixMoveEvent, HelixNavigateEvent, HelixNextEvent, HelixPreviousEvent, HelixReadEvent, HelixReconnectEvent, HelixRelabelEvent, HelixRemoveEvent, HelixReorderEvent, HelixResizeEvent, HelixSampleEndEvent, HelixSampleStartEvent, HelixScrolledEvent, HelixSelectEvent, HelixSendEvent, HelixShortcutEvent, HelixSortEvent, HelixTourAskEvent, type MapClusterDetail, type MapPanDetail, type MapPartChangeDetail, type MapRotateDetail, type MapTiltDetail, type MapZoomDetail, type MoveDetail, type NavigateDetail, type OpenDetail, type ReadDetail, type ReconnectDetail, type RelabelDetail, type RemoveDetail, type ResizeDetail, type ScrolledDetail, type SelectDetail, type SendDetail, type ShortcutDetail, type SkipDetail, type SortDetail, type ToggleDetail, type ViewportPanDetail, type ViewportZoomDetail, } from './events/events.js';
24
24
  export { HelixElement, whenHydrated } from './helix-element/helix-element.js';
25
25
  export { HelixFormElement } from './helix-form-element/helix-form-element.js';
26
26
  export { MixinChartSource, type MixinChartSourceInterface, PALETTE, PLOT_HEIGHT, PLOT_WIDTH, seriesIndex, seriesNames, } from './mixins/mixin-chart-source/mixin-chart-source.js';
package/dist/index.js CHANGED
@@ -20,7 +20,7 @@ export { SourceController, } from './controllers/source-controller/source-contro
20
20
  export { TokenColorController, } from './controllers/token-color-controller/token-color-controller.js';
21
21
  export { VirtualizeController, } from './controllers/virtualize-controller/virtualize-controller.js';
22
22
  export { eventsForTag, registerElementEvents } from './events/element-events.js';
23
- export { HelixActivateEvent, HelixBeforeActivateEvent, HelixBeforeChangeEvent, HelixBeforeCloseEvent, HelixBeforeCollapseEvent, HelixBeforeEditEvent, HelixBeforeMoveEvent, HelixBeforeOpenEvent, HelixBeforePanelResizeEvent, HelixBeforePinEvent, HelixBeforeReconnectEvent, HelixBeforeRelabelEvent, HelixBeforeResizeEvent, HelixBeforeSplitEvent, HelixBeforeToggleEvent, HelixCanvasEditEvent, HelixCanvasPanEvent, HelixCanvasViewEvent, HelixCanvasZoomEvent, HelixCaretEvent, HelixCompleteEvent, HelixConnectEvent, HelixConsoleEvent, HelixCopyEvent, HelixCreateEvent, HelixDismissEvent, HelixFacetEvent, HelixFilterEvent, HelixHistoryEvent, HelixMapClusterEvent, HelixMapPanEvent, HelixMapPartChangeEvent, HelixMapZoomEvent, HelixMoveEvent, HelixNavigateEvent, HelixNextEvent, HelixPreviousEvent, HelixReadEvent, HelixReconnectEvent, HelixRelabelEvent, HelixRemoveEvent, HelixReorderEvent, HelixResizeEvent, HelixSampleEndEvent, HelixSampleStartEvent, HelixScrolledEvent, HelixSelectEvent, HelixSendEvent, HelixShortcutEvent, HelixSortEvent, HelixTourAskEvent, } from './events/events.js';
23
+ export { HelixActivateEvent, HelixBeforeActivateEvent, HelixBeforeChangeEvent, HelixBeforeCloseEvent, HelixBeforeCollapseEvent, HelixBeforeEditEvent, HelixBeforeMoveEvent, HelixBeforeOpenEvent, HelixBeforePanelResizeEvent, HelixBeforePinEvent, HelixBeforeReconnectEvent, HelixBeforeRelabelEvent, HelixBeforeResizeEvent, HelixBeforeSplitEvent, HelixBeforeToggleEvent, HelixCanvasEditEvent, HelixCanvasPanEvent, HelixCanvasViewEvent, HelixCanvasZoomEvent, HelixCaretEvent, HelixCompleteEvent, HelixConnectEvent, HelixConsoleEvent, HelixCopyEvent, HelixCreateEvent, HelixDismissEvent, HelixFacetEvent, HelixFilterEvent, HelixHistoryEvent, HelixMapClusterEvent, HelixMapPanEvent, HelixMapPartChangeEvent, HelixMapRotateEvent, HelixMapTiltEvent, HelixMapZoomEvent, HelixMoveEvent, HelixNavigateEvent, HelixNextEvent, HelixPreviousEvent, HelixReadEvent, HelixReconnectEvent, HelixRelabelEvent, HelixRemoveEvent, HelixReorderEvent, HelixResizeEvent, HelixSampleEndEvent, HelixSampleStartEvent, HelixScrolledEvent, HelixSelectEvent, HelixSendEvent, HelixShortcutEvent, HelixSortEvent, HelixTourAskEvent, } from './events/events.js';
24
24
  export { HelixElement, whenHydrated } from './helix-element/helix-element.js';
25
25
  export { HelixFormElement } from './helix-form-element/helix-form-element.js';
26
26
  export { MixinChartSource, PALETTE, PLOT_HEIGHT, PLOT_WIDTH, seriesIndex, seriesNames, } from './mixins/mixin-chart-source/mixin-chart-source.js';
@@ -11,7 +11,7 @@ export type ViewportControlsPlacement = 'top-start' | 'top-end' | 'bottom-start'
11
11
  * The API a pan-and-zoom surface has, as TypeScript sees it.
12
12
  */
13
13
  export declare class ViewportElement {
14
- /** Which controls to show, as a list of `pan`, `zoom`, and `fullscreen`, or `none`. */
14
+ /** Which controls to show, as a list such as `pan zoom fullscreen`, or `none`. A family adds words of its own. */
15
15
  controls?: string;
16
16
  /** Which corner the controls sit in. */
17
17
  controlsPlacement?: ViewportControlsPlacement;
@@ -63,6 +63,16 @@ export declare class ViewportElement {
63
63
  protected get controlsElement(): Element | null;
64
64
  /** Which controls the surface draws while `controls` is absent. */
65
65
  protected get restingControls(): string;
66
+ /** Whether `controls`, or the resting controls, names a group. */
67
+ protected drawsControls(group: string): boolean;
68
+ /** More buttons for the group of controls, after zoom, for a family that adds its own. */
69
+ protected renderMoreControls(): TemplateResult | typeof nothing;
70
+ /** Answers a key the family adds, and says whether it did. */
71
+ protected handleMoreKeys(event: KeyboardEvent): boolean;
72
+ /** What one pan control is called, for a family that draws its own. */
73
+ protected panLabel(way: ViewportWay): string;
74
+ /** The arrow drawn on one pan control, for a family that draws its own. */
75
+ protected panGlyph(way: ViewportWay): string;
66
76
  }
67
77
  /**
68
78
  * Grafts a pan-and-zoom surface: its controls, its keys, and the way out.
@@ -40,6 +40,10 @@ export const MixinViewport = (Base) => {
40
40
  if (target !== own && !own.matches('[part~="controls"]')) {
41
41
  return;
42
42
  }
43
+ if (this.handleMoreKeys(event)) {
44
+ event.preventDefault();
45
+ return;
46
+ }
43
47
  const along = inlineStep(this, event.key);
44
48
  if (along === 1) {
45
49
  event.preventDefault();
@@ -88,9 +92,15 @@ export const MixinViewport = (Base) => {
88
92
  get restingControls() {
89
93
  return 'pan zoom fullscreen';
90
94
  }
91
- draws(group) {
95
+ drawsControls(group) {
92
96
  return (this.controls ?? this.restingControls).split(/\s+/).includes(group);
93
97
  }
98
+ renderMoreControls() {
99
+ return nothing;
100
+ }
101
+ handleMoreKeys(_event) {
102
+ return false;
103
+ }
94
104
  sideOf(way) {
95
105
  if (way !== 'start' && way !== 'end') {
96
106
  return way;
@@ -101,7 +111,7 @@ export const MixinViewport = (Base) => {
101
111
  const element = this;
102
112
  return !element.hydrating && isRtl(this);
103
113
  }
104
- named(way) {
114
+ panLabel(way) {
105
115
  const side = this.sideOf(way);
106
116
  return side === 'up'
107
117
  ? this.panUpLabel
@@ -111,7 +121,7 @@ export const MixinViewport = (Base) => {
111
121
  ? this.panLeftLabel
112
122
  : this.panRightLabel;
113
123
  }
114
- glyph(way) {
124
+ panGlyph(way) {
115
125
  return { down: '↓', left: '←', right: '→', up: '↑' }[this.sideOf(way)] ?? '';
116
126
  }
117
127
  firstUpdated(changed) {
@@ -126,25 +136,26 @@ export const MixinViewport = (Base) => {
126
136
  return this.renderRoot?.querySelector('[part="controls"]') ?? null;
127
137
  }
128
138
  renderControls() {
129
- const pan = this.draws('pan');
130
- const zoom = this.draws('zoom');
131
- const fullscreen = this.draws('fullscreen');
132
- if (!pan && !zoom && !fullscreen) {
139
+ const pan = this.drawsControls('pan');
140
+ const zoom = this.drawsControls('zoom');
141
+ const fullscreen = this.drawsControls('fullscreen');
142
+ const more = this.renderMoreControls();
143
+ if (!pan && !zoom && !fullscreen && more === nothing) {
133
144
  return nothing;
134
145
  }
135
146
  return html `<div part="controls" role="group" aria-label=${this.controlsLabel ?? this.restingControlsLabel}>
136
147
  ${pan
137
- ? html `<button part="pan-up" aria-label=${this.named('up')} type="button" @click=${() => this.pan('up')}>
138
- ${this.glyph('up')}
148
+ ? html `<button part="pan-up" aria-label=${this.panLabel('up')} type="button" @click=${() => this.pan('up')}>
149
+ ${this.panGlyph('up')}
139
150
  </button>
140
- <button part="pan-start" aria-label=${this.named('start')} type="button" @click=${() => this.pan('start')}>
141
- ${this.glyph('start')}
151
+ <button part="pan-start" aria-label=${this.panLabel('start')} type="button" @click=${() => this.pan('start')}>
152
+ ${this.panGlyph('start')}
142
153
  </button>
143
- <button part="pan-end" aria-label=${this.named('end')} type="button" @click=${() => this.pan('end')}>
144
- ${this.glyph('end')}
154
+ <button part="pan-end" aria-label=${this.panLabel('end')} type="button" @click=${() => this.pan('end')}>
155
+ ${this.panGlyph('end')}
145
156
  </button>
146
- <button part="pan-down" aria-label=${this.named('down')} type="button" @click=${() => this.pan('down')}>
147
- ${this.glyph('down')}
157
+ <button part="pan-down" aria-label=${this.panLabel('down')} type="button" @click=${() => this.pan('down')}>
158
+ ${this.panGlyph('down')}
148
159
  </button>`
149
160
  : nothing}
150
161
  ${zoom
@@ -155,6 +166,7 @@ export const MixinViewport = (Base) => {
155
166
  −
156
167
  </button>`
157
168
  : nothing}
169
+ ${more}
158
170
  ${fullscreen
159
171
  ? html `<button
160
172
  part="fullscreen"
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@fusion.dev/helix-core",
3
3
  "description": "Helix element primitives: base classes, mixins, decorators, utilities.",
4
- "version": "0.105.0",
4
+ "version": "0.106.0",
5
5
  "license": "MIT",
6
6
  "author": "Ross Sheppard <ross.sheppard@fusion.dev>",
7
7
  "repository": {
@@ -34,11 +34,11 @@
34
34
  "bugs": "https://github.com/project-fusion/fusion/issues",
35
35
  "dependencies": {
36
36
  "@floating-ui/dom": "^1.8.0",
37
- "@fusion.dev/helix-icons": "0.105.0"
37
+ "@fusion.dev/helix-icons": "0.106.0"
38
38
  },
39
39
  "devDependencies": {
40
- "@fusion.dev/helix-cem": "0.105.0",
41
- "@fusion.dev/helix-testing": "0.105.0",
40
+ "@fusion.dev/helix-cem": "0.106.0",
41
+ "@fusion.dev/helix-testing": "0.106.0",
42
42
  "@lit-labs/ssr": "^4.1.0",
43
43
  "lit": "^3.2.1"
44
44
  },