@rivet-health/design-system 40.14.0 → 40.16.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.
@@ -5526,47 +5526,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImpo
5526
5526
  CardActionsComponent.Orientations = ['horizontal', 'vertical'];
5527
5527
  })(CardActionsComponent || (CardActionsComponent = {}));
5528
5528
 
5529
- /**
5530
- * Body of a `riv-card`.
5531
- *
5532
- * Grows to absorb any spare height by default, which keeps `riv-card-actions`
5533
- * pinned to the bottom when cards of differing content sit in an equal-height
5534
- * grid. Set `[grow]="false"` when the card should shrink to its content.
5535
- */
5536
- class CardContentComponent {
5537
- constructor() {
5538
- this.padding = 'medium';
5539
- this.grow = true;
5540
- /** Draws a separator below the content. */
5541
- this.divider = false;
5542
- }
5543
- get classes() {
5544
- return Object.entries({
5545
- [`padding-${this.padding}`]: true,
5546
- grow: this.grow,
5547
- divider: this.divider,
5548
- })
5549
- .filter(([_, v]) => v)
5550
- .map(([k]) => k)
5551
- .join(' ');
5552
- }
5553
- }
5554
- CardContentComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: CardContentComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
5555
- CardContentComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: CardContentComponent, selector: "riv-card-content", inputs: { padding: "padding", grow: "grow", divider: "divider" }, host: { properties: { "class": "this.classes" } }, ngImport: i0, template: "<ng-content></ng-content>\n", styles: [":host{display:block;min-width:0;font:var(--body-medium)}:host.grow{flex-grow:1}:host.divider{border-bottom:var(--border-width) solid var(--border-light-blend)}:host.padding-small{padding:var(--size-small)}:host.padding-medium{padding:var(--size-medium)}:host.padding-large{padding:var(--size-large)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
5556
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: CardContentComponent, decorators: [{
5557
- type: Component,
5558
- args: [{ selector: 'riv-card-content', changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-content></ng-content>\n", styles: [":host{display:block;min-width:0;font:var(--body-medium)}:host.grow{flex-grow:1}:host.divider{border-bottom:var(--border-width) solid var(--border-light-blend)}:host.padding-small{padding:var(--size-small)}:host.padding-medium{padding:var(--size-medium)}:host.padding-large{padding:var(--size-large)}\n"] }]
5559
- }], propDecorators: { padding: [{
5560
- type: Input
5561
- }], grow: [{
5562
- type: Input
5563
- }], divider: [{
5564
- type: Input
5565
- }], classes: [{
5566
- type: HostBinding,
5567
- args: ['class']
5568
- }] } });
5569
-
5570
5529
  /**
5571
5530
  * Surface container that groups related content into a single scannable unit.
5572
5531
  *
@@ -5610,6 +5569,43 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImpo
5610
5569
  * When the card's action is really a navigation, prefer `[riv-card-link]` on an
5611
5570
  * `<a>` instead: that keeps the URL, middle-click, and "open in new tab".
5612
5571
  *
5572
+ * ## Selection
5573
+ *
5574
+ * Set `selected` to mark the card the user is currently looking at. It gives
5575
+ * the surface a distinct border and shadow, and announces through
5576
+ * `aria-current` so the state is not conveyed by colour alone.
5577
+ *
5578
+ * ```html
5579
+ * <riv-card [selected]="workbook.id === activeId">
5580
+ * ```
5581
+ *
5582
+ * Selection is separate from `tone`, so it composes with any tint — a selected
5583
+ * `danger` card keeps its danger background.
5584
+ *
5585
+ * ## Making the card expand and collapse
5586
+ *
5587
+ * Set `expandable` on `riv-card-header` and render the body only while it is
5588
+ * open. The header owns the toggle affordance and its accessibility; the
5589
+ * consumer owns the state:
5590
+ *
5591
+ * ```html
5592
+ * <riv-card>
5593
+ * <riv-card-header
5594
+ * [heading]="'Patient info'"
5595
+ * [expandable]="true"
5596
+ * [(expanded)]="open"
5597
+ * ></riv-card-header>
5598
+ * <riv-card-content *ngIf="open">...</riv-card-content>
5599
+ * </riv-card>
5600
+ * ```
5601
+ *
5602
+ * The card relays the region id from the header to the content, so the toggle's
5603
+ * `aria-controls` names the body with no ids wired by hand.
5604
+ *
5605
+ * Do not combine `expandable` with `clickable`: both claim the same click, and
5606
+ * a card that opens as well as expands leaves the user no way to tell which one
5607
+ * they are about to get.
5608
+ *
5613
5609
  * ## Notes
5614
5610
  *
5615
5611
  * When projecting raw content with no sub-components, set `padding` so the
@@ -5646,6 +5642,12 @@ class CardComponent {
5646
5642
  * affordance and keyboard support.
5647
5643
  */
5648
5644
  this.clickable = false;
5645
+ /**
5646
+ * Marks the card as the current selection, giving it a distinct border and
5647
+ * shadow. Composes with any `tone`, so a selected danger card keeps its
5648
+ * tint. Announced to assistive technology through `aria-current`.
5649
+ */
5650
+ this.selected = false;
5649
5651
  /**
5650
5652
  * Mutes the surface, suppresses hover feedback, and disables the click
5651
5653
  * target. Presentational for everything else — disable the card's own
@@ -5660,6 +5662,13 @@ class CardComponent {
5660
5662
  */
5661
5663
  this.cardId = '';
5662
5664
  }
5665
+ /**
5666
+ * Id an expandable header's toggle points `aria-controls` at, worn by a
5667
+ * projected `riv-card-content`.
5668
+ */
5669
+ get regionId() {
5670
+ return this.expandRegionId;
5671
+ }
5663
5672
  ngOnInit() {
5664
5673
  this.cardId = this.el.nativeElement.id || uniqueId('riv-card-');
5665
5674
  if (!this.el.nativeElement.id) {
@@ -5685,6 +5694,24 @@ class CardComponent {
5685
5694
  this.setTitleId(undefined);
5686
5695
  }
5687
5696
  }
5697
+ /**
5698
+ * Relays the region an expandable header controls to the projected
5699
+ * `riv-card-content`, so neither side needs the consumer to wire an id.
5700
+ * Called by `riv-card-header`; not part of the public API.
5701
+ */
5702
+ setRegionId(id) {
5703
+ this.expandRegionId = id;
5704
+ this.cdr.markForCheck();
5705
+ }
5706
+ /**
5707
+ * Gives up a region registered by `setRegionId`. Ignores ids that are no
5708
+ * longer the one in use.
5709
+ */
5710
+ clearRegionId(id) {
5711
+ if (this.expandRegionId === id) {
5712
+ this.setRegionId(undefined);
5713
+ }
5714
+ }
5688
5715
  /**
5689
5716
  * Element whose text names the click target, unless `clickLabel` is set. The
5690
5717
  * host id is read from the DOM rather than cached, so a bound `[id]` that
@@ -5696,6 +5723,10 @@ class CardComponent {
5696
5723
  return null;
5697
5724
  return (_a = this.titleId) !== null && _a !== void 0 ? _a : (this.el.nativeElement.id || this.cardId);
5698
5725
  }
5726
+ /** Announces the current selection to assistive technology. */
5727
+ get ariaCurrent() {
5728
+ return this.selected ? 'true' : null;
5729
+ }
5699
5730
  get classes() {
5700
5731
  return Object.entries({
5701
5732
  [this.variant]: true,
@@ -5704,6 +5735,7 @@ class CardComponent {
5704
5735
  [this.orientation]: true,
5705
5736
  [`padding-${this.padding}`]: true,
5706
5737
  clickable: this.clickable,
5738
+ selected: this.selected,
5707
5739
  disabled: this.disabled,
5708
5740
  })
5709
5741
  .filter(([_, v]) => v)
@@ -5712,10 +5744,10 @@ class CardComponent {
5712
5744
  }
5713
5745
  }
5714
5746
  CardComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: CardComponent, deps: [{ token: i0.ElementRef }, { token: i0.Renderer2 }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
5715
- CardComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: CardComponent, selector: "riv-card", inputs: { variant: "variant", surface: "surface", tone: "tone", orientation: "orientation", padding: "padding", clickable: "clickable", clickLabel: "clickLabel", disabled: "disabled" }, outputs: { cardClick: "cardClick" }, host: { properties: { "class": "this.classes" } }, ngImport: i0, template: "<button\n *ngIf=\"clickable\"\n type=\"button\"\n class=\"click-target\"\n [disabled]=\"disabled\"\n [attr.aria-label]=\"clickLabel\"\n [attr.aria-labelledby]=\"labelledBy\"\n (click)=\"cardClick.emit($event)\"\n></button>\n<ng-content></ng-content>\n", styles: [":host{position:relative;display:flex;min-width:0;overflow:hidden;border-radius:var(--card-border-radius, var(--border-radius-medium));color:var(--type-light-high-contrast);font:var(--body-medium)}:host.vertical{flex-direction:column}:host.horizontal{flex-direction:row}:host.outlined{border:var(--border-width) solid var(--border-light-blend)}:host.elevated{box-shadow:var(--depth-1)}:host.surface-0{background-color:var(--surface-light-0)}:host.surface-1{background-color:var(--surface-light-1)}:host.surface-2{background-color:var(--surface-light-2)}:host.tone-danger{background-color:var(--surface-light-danger)}:host.tone-caution{background-color:var(--surface-light-caution)}:host.tone-warning{background-color:var(--surface-light-warning)}:host.tone-safety{background-color:var(--surface-light-safety)}:host.tone-selected{background-color:var(--surface-light-selected)}:host.padding-small{padding:var(--size-small)}:host.padding-medium{padding:var(--size-medium)}:host.padding-large{padding:var(--size-large)}:host.clickable{transition:box-shadow var(--short-transition)}:host.clickable:not(.disabled):hover{box-shadow:var(--depth-1)}:host.elevated.clickable:not(.disabled):hover{box-shadow:var(--depth-2)}.click-target{position:absolute;inset:0;z-index:var(--card-click-target-z-index);padding:0;border:none;background:none;cursor:pointer;transition:background-color var(--short-transition)}.click-target:not(:disabled):hover{background-color:var(--black-20)}.click-target:disabled{cursor:default}.click-target:focus-visible{outline:var(--border-width-large) solid var(--type-light-link);outline-offset:calc(-1 * var(--border-width-large))}:host.disabled{background-color:var(--surface-light-1)}:host.outlined.disabled{border-color:var(--border-light)}@media print{:host{break-inside:avoid}}\n"], dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
5747
+ CardComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: CardComponent, selector: "riv-card", inputs: { variant: "variant", surface: "surface", tone: "tone", orientation: "orientation", padding: "padding", clickable: "clickable", clickLabel: "clickLabel", selected: "selected", disabled: "disabled" }, outputs: { cardClick: "cardClick" }, host: { properties: { "attr.aria-current": "this.ariaCurrent", "class": "this.classes" } }, ngImport: i0, template: "<button\n *ngIf=\"clickable\"\n type=\"button\"\n class=\"click-target\"\n [disabled]=\"disabled\"\n [attr.aria-label]=\"clickLabel\"\n [attr.aria-labelledby]=\"labelledBy\"\n (click)=\"cardClick.emit($event)\"\n></button>\n<ng-content></ng-content>\n", styles: [":host{position:relative;display:flex;min-width:0;overflow:hidden;border-radius:var(--card-border-radius, var(--border-radius-medium));color:var(--type-light-high-contrast);font:var(--body-medium)}:host.vertical{flex-direction:column}:host.horizontal{flex-direction:row}:host.outlined{border:var(--border-width) solid var(--border-light-blend)}:host.elevated{box-shadow:var(--depth-1)}:host.surface-0{background-color:var(--surface-light-0)}:host.surface-1{background-color:var(--surface-light-1)}:host.surface-2{background-color:var(--surface-light-2)}:host.tone-danger{background-color:var(--surface-light-danger)}:host.tone-caution{background-color:var(--surface-light-caution)}:host.tone-warning{background-color:var(--surface-light-warning)}:host.tone-safety{background-color:var(--surface-light-safety)}:host.tone-selected{background-color:var(--surface-light-selected)}:host.padding-small{padding:var(--size-small)}:host.padding-medium{padding:var(--size-medium)}:host.padding-large{padding:var(--size-large)}:host.clickable{transition:box-shadow var(--short-transition)}:host.clickable:not(.disabled):hover{box-shadow:var(--depth-1)}:host.elevated.clickable:not(.disabled):hover{box-shadow:var(--depth-2)}.click-target{position:absolute;inset:0;z-index:var(--card-click-target-z-index);padding:0;border:none;background:none;cursor:pointer;transition:background-color var(--short-transition)}.click-target:not(:disabled):hover{background-color:var(--black-20)}.click-target:disabled{cursor:default}.click-target:focus-visible{outline:var(--border-width-large) solid var(--type-light-link);outline-offset:calc(-1 * var(--border-width-large))}:host.selected{border:var(--border-width) solid var(--baloo-50);box-shadow:var(--depth-2)}:host.disabled{background-color:var(--surface-light-1)}:host.outlined.disabled{border-color:var(--border-light)}@media print{:host{break-inside:avoid}}\n"], dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
5716
5748
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: CardComponent, decorators: [{
5717
5749
  type: Component,
5718
- args: [{ selector: 'riv-card', changeDetection: ChangeDetectionStrategy.OnPush, template: "<button\n *ngIf=\"clickable\"\n type=\"button\"\n class=\"click-target\"\n [disabled]=\"disabled\"\n [attr.aria-label]=\"clickLabel\"\n [attr.aria-labelledby]=\"labelledBy\"\n (click)=\"cardClick.emit($event)\"\n></button>\n<ng-content></ng-content>\n", styles: [":host{position:relative;display:flex;min-width:0;overflow:hidden;border-radius:var(--card-border-radius, var(--border-radius-medium));color:var(--type-light-high-contrast);font:var(--body-medium)}:host.vertical{flex-direction:column}:host.horizontal{flex-direction:row}:host.outlined{border:var(--border-width) solid var(--border-light-blend)}:host.elevated{box-shadow:var(--depth-1)}:host.surface-0{background-color:var(--surface-light-0)}:host.surface-1{background-color:var(--surface-light-1)}:host.surface-2{background-color:var(--surface-light-2)}:host.tone-danger{background-color:var(--surface-light-danger)}:host.tone-caution{background-color:var(--surface-light-caution)}:host.tone-warning{background-color:var(--surface-light-warning)}:host.tone-safety{background-color:var(--surface-light-safety)}:host.tone-selected{background-color:var(--surface-light-selected)}:host.padding-small{padding:var(--size-small)}:host.padding-medium{padding:var(--size-medium)}:host.padding-large{padding:var(--size-large)}:host.clickable{transition:box-shadow var(--short-transition)}:host.clickable:not(.disabled):hover{box-shadow:var(--depth-1)}:host.elevated.clickable:not(.disabled):hover{box-shadow:var(--depth-2)}.click-target{position:absolute;inset:0;z-index:var(--card-click-target-z-index);padding:0;border:none;background:none;cursor:pointer;transition:background-color var(--short-transition)}.click-target:not(:disabled):hover{background-color:var(--black-20)}.click-target:disabled{cursor:default}.click-target:focus-visible{outline:var(--border-width-large) solid var(--type-light-link);outline-offset:calc(-1 * var(--border-width-large))}:host.disabled{background-color:var(--surface-light-1)}:host.outlined.disabled{border-color:var(--border-light)}@media print{:host{break-inside:avoid}}\n"] }]
5750
+ args: [{ selector: 'riv-card', changeDetection: ChangeDetectionStrategy.OnPush, template: "<button\n *ngIf=\"clickable\"\n type=\"button\"\n class=\"click-target\"\n [disabled]=\"disabled\"\n [attr.aria-label]=\"clickLabel\"\n [attr.aria-labelledby]=\"labelledBy\"\n (click)=\"cardClick.emit($event)\"\n></button>\n<ng-content></ng-content>\n", styles: [":host{position:relative;display:flex;min-width:0;overflow:hidden;border-radius:var(--card-border-radius, var(--border-radius-medium));color:var(--type-light-high-contrast);font:var(--body-medium)}:host.vertical{flex-direction:column}:host.horizontal{flex-direction:row}:host.outlined{border:var(--border-width) solid var(--border-light-blend)}:host.elevated{box-shadow:var(--depth-1)}:host.surface-0{background-color:var(--surface-light-0)}:host.surface-1{background-color:var(--surface-light-1)}:host.surface-2{background-color:var(--surface-light-2)}:host.tone-danger{background-color:var(--surface-light-danger)}:host.tone-caution{background-color:var(--surface-light-caution)}:host.tone-warning{background-color:var(--surface-light-warning)}:host.tone-safety{background-color:var(--surface-light-safety)}:host.tone-selected{background-color:var(--surface-light-selected)}:host.padding-small{padding:var(--size-small)}:host.padding-medium{padding:var(--size-medium)}:host.padding-large{padding:var(--size-large)}:host.clickable{transition:box-shadow var(--short-transition)}:host.clickable:not(.disabled):hover{box-shadow:var(--depth-1)}:host.elevated.clickable:not(.disabled):hover{box-shadow:var(--depth-2)}.click-target{position:absolute;inset:0;z-index:var(--card-click-target-z-index);padding:0;border:none;background:none;cursor:pointer;transition:background-color var(--short-transition)}.click-target:not(:disabled):hover{background-color:var(--black-20)}.click-target:disabled{cursor:default}.click-target:focus-visible{outline:var(--border-width-large) solid var(--type-light-link);outline-offset:calc(-1 * var(--border-width-large))}:host.selected{border:var(--border-width) solid var(--baloo-50);box-shadow:var(--depth-2)}:host.disabled{background-color:var(--surface-light-1)}:host.outlined.disabled{border-color:var(--border-light)}@media print{:host{break-inside:avoid}}\n"] }]
5719
5751
  }], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i0.Renderer2 }, { type: i0.ChangeDetectorRef }]; }, propDecorators: { variant: [{
5720
5752
  type: Input
5721
5753
  }], surface: [{
@@ -5730,10 +5762,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImpo
5730
5762
  type: Input
5731
5763
  }], clickLabel: [{
5732
5764
  type: Input
5765
+ }], selected: [{
5766
+ type: Input
5733
5767
  }], disabled: [{
5734
5768
  type: Input
5735
5769
  }], cardClick: [{
5736
5770
  type: Output
5771
+ }], ariaCurrent: [{
5772
+ type: HostBinding,
5773
+ args: ['attr.aria-current']
5737
5774
  }], classes: [{
5738
5775
  type: HostBinding,
5739
5776
  args: ['class']
@@ -5754,6 +5791,70 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImpo
5754
5791
  CardComponent.Paddings = ['none', 'small', 'medium', 'large'];
5755
5792
  })(CardComponent || (CardComponent = {}));
5756
5793
 
5794
+ /**
5795
+ * Body of a `riv-card`.
5796
+ *
5797
+ * Grows to absorb any spare height by default, which keeps `riv-card-actions`
5798
+ * pinned to the bottom when cards of differing content sit in an equal-height
5799
+ * grid. Set `[grow]="false"` when the card should shrink to its content.
5800
+ *
5801
+ * In a card with an expandable header, this is the region the toggle controls —
5802
+ * it takes an id from the card so that `aria-controls` resolves as soon as the
5803
+ * body is rendered.
5804
+ */
5805
+ class CardContentComponent {
5806
+ constructor(el, renderer, card) {
5807
+ this.el = el;
5808
+ this.renderer = renderer;
5809
+ this.card = card;
5810
+ this.padding = 'medium';
5811
+ this.grow = true;
5812
+ /** Draws a separator below the content. */
5813
+ this.divider = false;
5814
+ }
5815
+ /**
5816
+ * Wears the region id an expandable header's toggle points `aria-controls`
5817
+ * at, so neither side needs the consumer to name the body. An id the consumer
5818
+ * set of their own is left alone.
5819
+ */
5820
+ ngOnInit() {
5821
+ var _a;
5822
+ const regionId = (_a = this.card) === null || _a === void 0 ? void 0 : _a.regionId;
5823
+ if (regionId && !this.el.nativeElement.id) {
5824
+ this.renderer.setAttribute(this.el.nativeElement, 'id', regionId);
5825
+ }
5826
+ }
5827
+ get classes() {
5828
+ return Object.entries({
5829
+ [`padding-${this.padding}`]: true,
5830
+ grow: this.grow,
5831
+ divider: this.divider,
5832
+ })
5833
+ .filter(([_, v]) => v)
5834
+ .map(([k]) => k)
5835
+ .join(' ');
5836
+ }
5837
+ }
5838
+ CardContentComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: CardContentComponent, deps: [{ token: i0.ElementRef }, { token: i0.Renderer2 }, { token: CardComponent, optional: true }], target: i0.ɵɵFactoryTarget.Component });
5839
+ CardContentComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: CardContentComponent, selector: "riv-card-content", inputs: { padding: "padding", grow: "grow", divider: "divider" }, host: { properties: { "class": "this.classes" } }, ngImport: i0, template: "<ng-content></ng-content>\n", styles: [":host{display:block;min-width:0;font:var(--body-medium)}:host.grow{flex-grow:1}:host.divider{border-bottom:var(--border-width) solid var(--border-light-blend)}:host.padding-small{padding:var(--size-small)}:host.padding-medium{padding:var(--size-medium)}:host.padding-large{padding:var(--size-large)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
5840
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: CardContentComponent, decorators: [{
5841
+ type: Component,
5842
+ args: [{ selector: 'riv-card-content', changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-content></ng-content>\n", styles: [":host{display:block;min-width:0;font:var(--body-medium)}:host.grow{flex-grow:1}:host.divider{border-bottom:var(--border-width) solid var(--border-light-blend)}:host.padding-small{padding:var(--size-small)}:host.padding-medium{padding:var(--size-medium)}:host.padding-large{padding:var(--size-large)}\n"] }]
5843
+ }], ctorParameters: function () {
5844
+ return [{ type: i0.ElementRef }, { type: i0.Renderer2 }, { type: CardComponent, decorators: [{
5845
+ type: Optional
5846
+ }] }];
5847
+ }, propDecorators: { padding: [{
5848
+ type: Input
5849
+ }], grow: [{
5850
+ type: Input
5851
+ }], divider: [{
5852
+ type: Input
5853
+ }], classes: [{
5854
+ type: HostBinding,
5855
+ args: ['class']
5856
+ }] } });
5857
+
5757
5858
  /**
5758
5859
  * Top row of a `riv-card`: heading text plus optional adornments.
5759
5860
  *
@@ -5777,8 +5878,33 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImpo
5777
5878
  * outline; pick the level that follows the surrounding content rather than the
5778
5879
  * one that looks right, since size comes from the design tokens either way.
5779
5880
  *
5780
- * The heading font can be overridden per-instance with the
5781
- * `--card-heading-font` custom property.
5881
+ * The heading font and colour can be overridden per-instance with the
5882
+ * `--card-heading-font` and `--card-heading-color` custom properties.
5883
+ *
5884
+ * ## Expanding and collapsing
5885
+ *
5886
+ * `expandable` turns the header into the toggle for the card's body: it renders
5887
+ * a chevron and covers the row with a real `<button>` carrying `aria-expanded`
5888
+ * and `aria-controls`, so the pattern is keyboard operable and announces its
5889
+ * state. Controls in `[actions]` sit above that button and stay independently
5890
+ * clickable, so a menu beside the toggle needs no `stopPropagation`.
5891
+ *
5892
+ * The header holds no state of its own — bind `expanded` and act on
5893
+ * `expandedChange`, or use `[(expanded)]`:
5894
+ *
5895
+ * ```html
5896
+ * <riv-card>
5897
+ * <riv-card-header
5898
+ * [heading]="'Patient info'"
5899
+ * [expandable]="true"
5900
+ * [(expanded)]="open"
5901
+ * ></riv-card-header>
5902
+ * <riv-card-content *ngIf="open">...</riv-card-content>
5903
+ * </riv-card>
5904
+ * ```
5905
+ *
5906
+ * The toggle is named by the header's title, so an expandable header needs a
5907
+ * `heading` or a projected title.
5782
5908
  */
5783
5909
  class CardHeaderComponent {
5784
5910
  constructor(card) {
@@ -5788,6 +5914,11 @@ class CardHeaderComponent {
5788
5914
  * target from the title rather than from the card's whole text.
5789
5915
  */
5790
5916
  this.titleId = uniqueId('riv-card-title-');
5917
+ /**
5918
+ * Names the body the toggle controls. Handed to the enclosing card, which
5919
+ * relays it to the projected `riv-card-content`.
5920
+ */
5921
+ this.regionId = uniqueId('riv-card-region-');
5791
5922
  /** Heading element rendered for `heading`, for document outline purposes. */
5792
5923
  this.headingLevel = 3;
5793
5924
  /** `filled` gives the header its own background, separating it from the body. */
@@ -5797,14 +5928,29 @@ class CardHeaderComponent {
5797
5928
  this.divider = false;
5798
5929
  /** Cross-axis alignment of the leading slot, heading block, and actions. */
5799
5930
  this.align = 'start';
5931
+ /** Turns the header row into the toggle for the card's body. */
5932
+ this.expandable = false;
5933
+ /** Whether the body is currently open. The consumer owns this state. */
5934
+ this.expanded = false;
5935
+ /** Side of the header the chevron sits on. */
5936
+ this.togglePosition = 'trailing';
5937
+ /**
5938
+ * Blocks the toggle, for a section that is not yet available to open. Set it
5939
+ * alongside a disabled `riv-card`, whose own `disabled` mutes the surface but
5940
+ * does not reach the header's controls.
5941
+ */
5942
+ this.disabled = false;
5943
+ /** Emitted with the state the consumer is being asked to move to. */
5944
+ this.expandedChange = new EventEmitter();
5800
5945
  }
5801
5946
  /**
5802
- * Offers the heading to an enclosing card as the name for its click target.
5803
- * Only a text heading is offered: projected titles can be anything, so those
5804
- * cards fall back to naming against their whole content.
5947
+ * Offers the heading to an enclosing card as the name for its click target,
5948
+ * and the toggle's region so a projected `riv-card-content` can wear it. Only
5949
+ * a text heading is offered: projected titles can be anything, so those cards
5950
+ * fall back to naming against their whole content.
5805
5951
  */
5806
5952
  ngOnChanges(changes) {
5807
- var _a, _b, _c;
5953
+ var _a, _b, _c, _d, _e;
5808
5954
  if (changes['heading']) {
5809
5955
  if ((_a = this.heading) === null || _a === void 0 ? void 0 : _a.trim()) {
5810
5956
  (_b = this.card) === null || _b === void 0 ? void 0 : _b.setTitleId(this.titleId);
@@ -5813,11 +5959,27 @@ class CardHeaderComponent {
5813
5959
  (_c = this.card) === null || _c === void 0 ? void 0 : _c.clearTitleId(this.titleId);
5814
5960
  }
5815
5961
  }
5962
+ if (changes['expandable']) {
5963
+ if (this.expandable) {
5964
+ (_d = this.card) === null || _d === void 0 ? void 0 : _d.setRegionId(this.regionId);
5965
+ }
5966
+ else {
5967
+ (_e = this.card) === null || _e === void 0 ? void 0 : _e.clearRegionId(this.regionId);
5968
+ }
5969
+ }
5816
5970
  }
5817
5971
  /** Withdraws the offer, so the card stops naming itself against this header. */
5818
5972
  ngOnDestroy() {
5819
- var _a;
5973
+ var _a, _b;
5820
5974
  (_a = this.card) === null || _a === void 0 ? void 0 : _a.clearTitleId(this.titleId);
5975
+ (_b = this.card) === null || _b === void 0 ? void 0 : _b.clearRegionId(this.regionId);
5976
+ }
5977
+ /**
5978
+ * Asks for the opposite state rather than taking it, which keeps the header
5979
+ * free of state that could drift from the consumer's.
5980
+ */
5981
+ toggle() {
5982
+ this.expandedChange.emit(!this.expanded);
5821
5983
  }
5822
5984
  get classes() {
5823
5985
  return Object.entries({
@@ -5825,6 +5987,7 @@ class CardHeaderComponent {
5825
5987
  [`padding-${this.padding}`]: true,
5826
5988
  [`align-${this.align}`]: true,
5827
5989
  divider: this.divider,
5990
+ expandable: this.expandable,
5828
5991
  })
5829
5992
  .filter(([_, v]) => v)
5830
5993
  .map(([k]) => k)
@@ -5846,10 +6009,10 @@ class CardHeaderComponent {
5846
6009
  }
5847
6010
  }
5848
6011
  CardHeaderComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: CardHeaderComponent, deps: [{ token: CardComponent, optional: true }], target: i0.ɵɵFactoryTarget.Component });
5849
- CardHeaderComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: CardHeaderComponent, selector: "riv-card-header", inputs: { heading: "heading", subheading: "subheading", headingLevel: "headingLevel", variant: "variant", padding: "padding", divider: "divider", align: "align", clamp: "clamp" }, host: { properties: { "class": "this.classes" } }, usesOnChanges: true, ngImport: i0, template: "<div class=\"leading\"><ng-content select=\"[leading]\"></ng-content></div>\n<div class=\"main\">\n <div class=\"title-row\" [id]=\"titleId\">\n <div *ngIf=\"heading?.trim()\" class=\"heading\" [ngStyle]=\"clampStyle\">\n <ng-container [ngSwitch]=\"headingLevel\">\n <h1 *ngSwitchCase=\"1\">{{ heading }}</h1>\n <h2 *ngSwitchCase=\"2\">{{ heading }}</h2>\n <h3 *ngSwitchCase=\"3\">{{ heading }}</h3>\n <h4 *ngSwitchCase=\"4\">{{ heading }}</h4>\n <h5 *ngSwitchCase=\"5\">{{ heading }}</h5>\n <h6 *ngSwitchCase=\"6\">{{ heading }}</h6>\n </ng-container>\n </div>\n <ng-content></ng-content>\n </div>\n <div *ngIf=\"subheading?.trim()\" class=\"subheading\">{{ subheading }}</div>\n</div>\n<div class=\"actions\"><ng-content select=\"[actions]\"></ng-content></div>\n", styles: [":host{display:flex;gap:var(--size-medium);min-width:0}:host.align-start{align-items:flex-start}:host.align-center{align-items:center}:host.padding-small{padding:var(--size-small)}:host.padding-medium{padding:var(--size-medium)}:host.padding-large{padding:var(--size-large)}:host.filled{background-color:var(--surface-light-1)}:host.filled,:host.divider{border-bottom:var(--border-width) solid var(--border-light-blend)}.main{flex-grow:1;min-width:0}.title-row{display:flex;flex-wrap:wrap;align-items:baseline;gap:var(--size-xsmall);min-width:0;font:var(--card-heading-font, var(--title-04));color:var(--type-light-high-contrast)}:host.filled .title-row{font:var(--card-heading-font, var(--title-02))}.leading{flex-shrink:0}.actions{position:relative;z-index:var(--card-raised-z-index);display:flex;flex-shrink:0;align-items:center;gap:var(--size-small)}.leading:not(:has(*)),.actions:not(:has(*)){display:none}.heading{min-width:0}.heading>*{display:inline;margin:0;font:inherit;color:inherit}.subheading{font:var(--body-small);color:var(--type-light-low-contrast)}\n"], dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i1.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i1.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
6012
+ CardHeaderComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: CardHeaderComponent, selector: "riv-card-header", inputs: { heading: "heading", subheading: "subheading", headingLevel: "headingLevel", variant: "variant", padding: "padding", divider: "divider", align: "align", clamp: "clamp", expandable: "expandable", expanded: "expanded", togglePosition: "togglePosition", disabled: "disabled" }, outputs: { expandedChange: "expandedChange" }, host: { properties: { "class": "this.classes" } }, usesOnChanges: true, ngImport: i0, template: "<button\n *ngIf=\"expandable\"\n type=\"button\"\n class=\"expand-toggle\"\n [disabled]=\"disabled\"\n [attr.aria-expanded]=\"expanded\"\n [attr.aria-controls]=\"regionId\"\n [attr.aria-labelledby]=\"titleId\"\n (click)=\"toggle()\"\n></button>\n<ng-container\n *ngIf=\"expandable && togglePosition === 'leading'\"\n [ngTemplateOutlet]=\"chevron\"\n></ng-container>\n<div class=\"leading\"><ng-content select=\"[leading]\"></ng-content></div>\n<div class=\"main\">\n <div class=\"title-row\" [id]=\"titleId\">\n <div *ngIf=\"heading?.trim()\" class=\"heading\" [ngStyle]=\"clampStyle\">\n <ng-container [ngSwitch]=\"headingLevel\">\n <h1 *ngSwitchCase=\"1\">{{ heading }}</h1>\n <h2 *ngSwitchCase=\"2\">{{ heading }}</h2>\n <h3 *ngSwitchCase=\"3\">{{ heading }}</h3>\n <h4 *ngSwitchCase=\"4\">{{ heading }}</h4>\n <h5 *ngSwitchCase=\"5\">{{ heading }}</h5>\n <h6 *ngSwitchCase=\"6\">{{ heading }}</h6>\n </ng-container>\n </div>\n <ng-content></ng-content>\n </div>\n <div *ngIf=\"subheading?.trim()\" class=\"subheading\">{{ subheading }}</div>\n</div>\n<ng-container\n *ngIf=\"expandable && togglePosition === 'trailing'\"\n [ngTemplateOutlet]=\"chevron\"\n></ng-container>\n<div class=\"actions\"><ng-content select=\"[actions]\"></ng-content></div>\n\n<ng-template #chevron>\n <riv-icon\n class=\"expand-chevron\"\n [class.expanded]=\"expanded\"\n [name]=\"'ChevronDown'\"\n [size]=\"20\"\n ></riv-icon>\n</ng-template>\n", styles: [":host{display:flex;gap:var(--size-medium);min-width:0}:host.expandable{position:relative}:host.align-start{align-items:flex-start}:host.align-center{align-items:center}:host.padding-small{padding:var(--size-small)}:host.padding-medium{padding:var(--size-medium)}:host.padding-large{padding:var(--size-large)}:host.filled{background-color:var(--surface-light-1)}:host.filled,:host.divider{border-bottom:var(--border-width) solid var(--border-light-blend)}.main{flex-grow:1;min-width:0}.title-row{display:flex;flex-wrap:wrap;align-items:baseline;gap:var(--size-xsmall);min-width:0;font:var(--card-heading-font, var(--title-04));color:var(--card-heading-color, var(--type-light-high-contrast))}:host.filled .title-row{font:var(--card-heading-font, var(--title-02))}.leading{flex-shrink:0}.actions{position:relative;z-index:var(--card-raised-z-index);display:flex;flex-shrink:0;align-items:center;gap:var(--size-small)}.expand-toggle{position:absolute;inset:0;z-index:var(--card-click-target-z-index);padding:0;border:none;background:none;cursor:pointer;transition:background-color var(--short-transition)}.expand-toggle:not(:disabled):hover{background-color:var(--black-20)}.expand-toggle:disabled{cursor:default}.expand-toggle:focus-visible{outline:var(--border-width-large) solid var(--type-light-link);outline-offset:calc(-1 * var(--border-width-large))}.expand-chevron{flex-shrink:0;transition:transform var(--medium-transition)}.expand-chevron.expanded{transform:rotate(-180deg)}.leading:not(:has(*)),.actions:not(:has(*)){display:none}.heading{min-width:0}.heading>*{display:inline;margin:0;font:inherit;color:inherit}.subheading{font:var(--body-small);color:var(--type-light-low-contrast)}\n"], dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i1.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i1.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "component", type: IconComponent, selector: "riv-icon", inputs: ["name", "fillColor", "size", "customSize", "strokeWidth"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
5850
6013
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: CardHeaderComponent, decorators: [{
5851
6014
  type: Component,
5852
- args: [{ selector: 'riv-card-header', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"leading\"><ng-content select=\"[leading]\"></ng-content></div>\n<div class=\"main\">\n <div class=\"title-row\" [id]=\"titleId\">\n <div *ngIf=\"heading?.trim()\" class=\"heading\" [ngStyle]=\"clampStyle\">\n <ng-container [ngSwitch]=\"headingLevel\">\n <h1 *ngSwitchCase=\"1\">{{ heading }}</h1>\n <h2 *ngSwitchCase=\"2\">{{ heading }}</h2>\n <h3 *ngSwitchCase=\"3\">{{ heading }}</h3>\n <h4 *ngSwitchCase=\"4\">{{ heading }}</h4>\n <h5 *ngSwitchCase=\"5\">{{ heading }}</h5>\n <h6 *ngSwitchCase=\"6\">{{ heading }}</h6>\n </ng-container>\n </div>\n <ng-content></ng-content>\n </div>\n <div *ngIf=\"subheading?.trim()\" class=\"subheading\">{{ subheading }}</div>\n</div>\n<div class=\"actions\"><ng-content select=\"[actions]\"></ng-content></div>\n", styles: [":host{display:flex;gap:var(--size-medium);min-width:0}:host.align-start{align-items:flex-start}:host.align-center{align-items:center}:host.padding-small{padding:var(--size-small)}:host.padding-medium{padding:var(--size-medium)}:host.padding-large{padding:var(--size-large)}:host.filled{background-color:var(--surface-light-1)}:host.filled,:host.divider{border-bottom:var(--border-width) solid var(--border-light-blend)}.main{flex-grow:1;min-width:0}.title-row{display:flex;flex-wrap:wrap;align-items:baseline;gap:var(--size-xsmall);min-width:0;font:var(--card-heading-font, var(--title-04));color:var(--type-light-high-contrast)}:host.filled .title-row{font:var(--card-heading-font, var(--title-02))}.leading{flex-shrink:0}.actions{position:relative;z-index:var(--card-raised-z-index);display:flex;flex-shrink:0;align-items:center;gap:var(--size-small)}.leading:not(:has(*)),.actions:not(:has(*)){display:none}.heading{min-width:0}.heading>*{display:inline;margin:0;font:inherit;color:inherit}.subheading{font:var(--body-small);color:var(--type-light-low-contrast)}\n"] }]
6015
+ args: [{ selector: 'riv-card-header', changeDetection: ChangeDetectionStrategy.OnPush, template: "<button\n *ngIf=\"expandable\"\n type=\"button\"\n class=\"expand-toggle\"\n [disabled]=\"disabled\"\n [attr.aria-expanded]=\"expanded\"\n [attr.aria-controls]=\"regionId\"\n [attr.aria-labelledby]=\"titleId\"\n (click)=\"toggle()\"\n></button>\n<ng-container\n *ngIf=\"expandable && togglePosition === 'leading'\"\n [ngTemplateOutlet]=\"chevron\"\n></ng-container>\n<div class=\"leading\"><ng-content select=\"[leading]\"></ng-content></div>\n<div class=\"main\">\n <div class=\"title-row\" [id]=\"titleId\">\n <div *ngIf=\"heading?.trim()\" class=\"heading\" [ngStyle]=\"clampStyle\">\n <ng-container [ngSwitch]=\"headingLevel\">\n <h1 *ngSwitchCase=\"1\">{{ heading }}</h1>\n <h2 *ngSwitchCase=\"2\">{{ heading }}</h2>\n <h3 *ngSwitchCase=\"3\">{{ heading }}</h3>\n <h4 *ngSwitchCase=\"4\">{{ heading }}</h4>\n <h5 *ngSwitchCase=\"5\">{{ heading }}</h5>\n <h6 *ngSwitchCase=\"6\">{{ heading }}</h6>\n </ng-container>\n </div>\n <ng-content></ng-content>\n </div>\n <div *ngIf=\"subheading?.trim()\" class=\"subheading\">{{ subheading }}</div>\n</div>\n<ng-container\n *ngIf=\"expandable && togglePosition === 'trailing'\"\n [ngTemplateOutlet]=\"chevron\"\n></ng-container>\n<div class=\"actions\"><ng-content select=\"[actions]\"></ng-content></div>\n\n<ng-template #chevron>\n <riv-icon\n class=\"expand-chevron\"\n [class.expanded]=\"expanded\"\n [name]=\"'ChevronDown'\"\n [size]=\"20\"\n ></riv-icon>\n</ng-template>\n", styles: [":host{display:flex;gap:var(--size-medium);min-width:0}:host.expandable{position:relative}:host.align-start{align-items:flex-start}:host.align-center{align-items:center}:host.padding-small{padding:var(--size-small)}:host.padding-medium{padding:var(--size-medium)}:host.padding-large{padding:var(--size-large)}:host.filled{background-color:var(--surface-light-1)}:host.filled,:host.divider{border-bottom:var(--border-width) solid var(--border-light-blend)}.main{flex-grow:1;min-width:0}.title-row{display:flex;flex-wrap:wrap;align-items:baseline;gap:var(--size-xsmall);min-width:0;font:var(--card-heading-font, var(--title-04));color:var(--card-heading-color, var(--type-light-high-contrast))}:host.filled .title-row{font:var(--card-heading-font, var(--title-02))}.leading{flex-shrink:0}.actions{position:relative;z-index:var(--card-raised-z-index);display:flex;flex-shrink:0;align-items:center;gap:var(--size-small)}.expand-toggle{position:absolute;inset:0;z-index:var(--card-click-target-z-index);padding:0;border:none;background:none;cursor:pointer;transition:background-color var(--short-transition)}.expand-toggle:not(:disabled):hover{background-color:var(--black-20)}.expand-toggle:disabled{cursor:default}.expand-toggle:focus-visible{outline:var(--border-width-large) solid var(--type-light-link);outline-offset:calc(-1 * var(--border-width-large))}.expand-chevron{flex-shrink:0;transition:transform var(--medium-transition)}.expand-chevron.expanded{transform:rotate(-180deg)}.leading:not(:has(*)),.actions:not(:has(*)){display:none}.heading{min-width:0}.heading>*{display:inline;margin:0;font:inherit;color:inherit}.subheading{font:var(--body-small);color:var(--type-light-low-contrast)}\n"] }]
5853
6016
  }], ctorParameters: function () {
5854
6017
  return [{ type: CardComponent, decorators: [{
5855
6018
  type: Optional
@@ -5870,6 +6033,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImpo
5870
6033
  type: Input
5871
6034
  }], clamp: [{
5872
6035
  type: Input
6036
+ }], expandable: [{
6037
+ type: Input
6038
+ }], expanded: [{
6039
+ type: Input
6040
+ }], togglePosition: [{
6041
+ type: Input
6042
+ }], disabled: [{
6043
+ type: Input
6044
+ }], expandedChange: [{
6045
+ type: Output
5873
6046
  }], classes: [{
5874
6047
  type: HostBinding,
5875
6048
  args: ['class']
@@ -5878,6 +6051,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImpo
5878
6051
  CardHeaderComponent.Variants = ['plain', 'filled'];
5879
6052
  CardHeaderComponent.HeadingLevels = [1, 2, 3, 4, 5, 6];
5880
6053
  CardHeaderComponent.Aligns = ['start', 'center'];
6054
+ CardHeaderComponent.TogglePositions = ['leading', 'trailing'];
5881
6055
  })(CardHeaderComponent || (CardHeaderComponent = {}));
5882
6056
 
5883
6057
  /**