@rivet-health/design-system 40.7.1 → 40.9.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.
@@ -4308,18 +4308,20 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImpo
4308
4308
  'large',
4309
4309
  'xlarge',
4310
4310
  ];
4311
- // Accepts MM/DD/YYYY, M/D/YY, and YYYY-MM-DD with /, -, or . separators.
4312
- // A 4-digit leading segment is treated as year.
4311
+ // Accepts MM/DD/YYYY, M/D/YY, and YYYY-MM-DD with /, -, or . separators, as
4312
+ // well as separator-less digit runs (MMDDYYYY or MMDDYY). A 4-digit leading
4313
+ // segment in the separated form is treated as an ISO year; 2-digit years
4314
+ // pivot at 50 (<50 → 2000s, >=50 → 1900s).
4313
4315
  function parseDate(input) {
4314
- const parts = input.trim().split(/[/.-]/);
4315
- if (parts.length !== 3)
4316
+ const trimmed = input.trim();
4317
+ if (!trimmed)
4316
4318
  return undefined;
4317
- const nums = parts.map(p => parseInt(p, 10));
4318
- if (nums.some(isNaN))
4319
+ const ymd = /^\d+$/.test(trimmed)
4320
+ ? parseDigits(trimmed)
4321
+ : parseSeparated(trimmed);
4322
+ if (!ymd)
4319
4323
  return undefined;
4320
- let [year, month, day] = parts[0].length === 4 ? nums : [nums[2], nums[0], nums[1]];
4321
- if (parts[0].length !== 4 && year < 100)
4322
- year += year < 50 ? 2000 : 1900;
4324
+ const [year, month, day] = ymd;
4323
4325
  const date = new Date(year, month - 1, day);
4324
4326
  return date.getFullYear() === year &&
4325
4327
  date.getMonth() === month - 1 &&
@@ -4328,6 +4330,35 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImpo
4328
4330
  : undefined;
4329
4331
  }
4330
4332
  DateComponent.parseDate = parseDate;
4333
+ function expandTwoDigitYear(year) {
4334
+ return year < 100 ? year + (year < 50 ? 2000 : 1900) : year;
4335
+ }
4336
+ // A run of digits with no separators: 8 → MMDDYYYY (literal year), 6 → MMDDYY
4337
+ // (2-digit year pivoted at 50).
4338
+ function parseDigits(digits) {
4339
+ if (digits.length !== 8 && digits.length !== 6)
4340
+ return undefined;
4341
+ const year = parseInt(digits.slice(4), 10);
4342
+ return [
4343
+ digits.length === 6 ? expandTwoDigitYear(year) : year,
4344
+ parseInt(digits.slice(0, 2), 10),
4345
+ parseInt(digits.slice(2, 4), 10),
4346
+ ];
4347
+ }
4348
+ function parseSeparated(input) {
4349
+ const parts = input.split(/[/.-]/);
4350
+ if (parts.length !== 3)
4351
+ return undefined;
4352
+ const nums = parts.map(p => parseInt(p, 10));
4353
+ if (nums.some(isNaN))
4354
+ return undefined;
4355
+ const [year, month, day] = parts[0].length === 4 ? nums : [nums[2], nums[0], nums[1]];
4356
+ return [
4357
+ parts[0].length === 4 ? year : expandTwoDigitYear(year),
4358
+ month,
4359
+ day,
4360
+ ];
4361
+ }
4331
4362
  function isValidDate(date, min, max) {
4332
4363
  const time = date.getTime();
4333
4364
  return time >= min.getTime() && time <= max.getTime();
@@ -5443,12 +5474,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImpo
5443
5474
  * "commit" framing over "change" is deliberate: consumers that persist on
5444
5475
  * commit (analytics, PATCH) treat the gesture itself as the event; those
5445
5476
  * that only care about diffs can compare `next === current` in the handler.
5446
- * There is no cancel path.
5447
5477
  *
5448
- * Outside-click commits (rather than cancels) to match the established DS
5449
- * interaction pattern in `riv-date` and `riv-select`. If a future consumer
5450
- * needs an abandon path, the soft first step is Escape-to-cancel rather
5451
- * than changing outside-click's meaning.
5478
+ * Escape cancels: reverts the draft back to the current value and drops out
5479
+ * of edit mode WITHOUT emitting a change. Outside-click still commits (to
5480
+ * match `riv-date` / `riv-select`); Escape is the deliberate abandon path.
5452
5481
  *
5453
5482
  * Value handling follows the same convention as `riv-text-field`: `[title]`
5454
5483
  * and `[description]` are treated as suggestions, not authoritative state.
@@ -5457,6 +5486,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImpo
5457
5486
  * a different value. That lets the component demo out of the box (e.g. in
5458
5487
  * Storybook) without requiring a round trip through the parent.
5459
5488
  *
5489
+ * Programmatic entry into edit mode is available via `startEditingTitle()`
5490
+ * and `startEditingDescription()` — obtain a reference with
5491
+ * `@ViewChild(EditableHeadingComponent)`. Use these for secondary entry
5492
+ * points like a kebab-menu "Rename" item, or to auto-open rename after
5493
+ * creating a new entity.
5494
+ *
5460
5495
  * The component owns no border, background, or card chrome — consumers who
5461
5496
  * want a bordered container should wrap it.
5462
5497
  *
@@ -5493,6 +5528,15 @@ class EditableHeadingComponent {
5493
5528
  get description() {
5494
5529
  return this._description;
5495
5530
  }
5531
+ set disabled(v) {
5532
+ this._disabled = v;
5533
+ if (v && this.mode !== 'view')
5534
+ this.cancel();
5535
+ this.cdr.markForCheck();
5536
+ }
5537
+ get disabled() {
5538
+ return this._disabled;
5539
+ }
5496
5540
  constructor(cdr, host) {
5497
5541
  this.cdr = cdr;
5498
5542
  this.host = host;
@@ -5501,6 +5545,13 @@ class EditableHeadingComponent {
5501
5545
  this._description = '';
5502
5546
  this.titleMaxLength = 100;
5503
5547
  this.descriptionMaxLength = 300;
5548
+ /**
5549
+ * When true, the title and description are not clickable and cannot be
5550
+ * edited. Any in-progress edit is dropped without emitting. Imperative
5551
+ * `startEditing*` calls no-op. Useful for consumers that need to gate
5552
+ * editing on a permission or a broader "actions disabled" state.
5553
+ */
5554
+ this._disabled = false;
5504
5555
  this.titleChange = new EventEmitter();
5505
5556
  this.descriptionChange = new EventEmitter();
5506
5557
  this.mode = 'view';
@@ -5510,15 +5561,38 @@ class EditableHeadingComponent {
5510
5561
  get isEditing() {
5511
5562
  return this.mode !== 'view';
5512
5563
  }
5564
+ /**
5565
+ * Enter title-edit mode programmatically. Same behavior as clicking the
5566
+ * title — populates the edit buffer from the current value, focuses the
5567
+ * input with the caret at the end, and hides the actions slot. No-op if
5568
+ * already in an editing mode or when the component is disabled.
5569
+ *
5570
+ * Intended for consumers that offer a secondary entry point to renaming
5571
+ * (e.g. a kebab menu item), or that want to auto-open rename after
5572
+ * creating a new entity. Bind via `@ViewChild(EditableHeadingComponent)`.
5573
+ */
5513
5574
  startEditingTitle() {
5575
+ if (this.disabled || this.mode !== 'view')
5576
+ return;
5514
5577
  this.draftTitle = this.title;
5515
5578
  this.mode = 'editingTitle';
5579
+ // `markForCheck` so consumers can call this from outside Angular's zone
5580
+ // (e.g. an async callback) without having to trigger CD themselves.
5581
+ this.cdr.markForCheck();
5516
5582
  // Defer focus until Angular has rendered the input into the DOM.
5517
5583
  setTimeout(() => this.focusEnd(this.titleInputRef), 0);
5518
5584
  }
5585
+ /**
5586
+ * Enter description-edit mode programmatically. Same behavior as clicking
5587
+ * the description (or the "+ Add description" button). No-op if already
5588
+ * in an editing mode or when the component is disabled.
5589
+ */
5519
5590
  startEditingDescription() {
5591
+ if (this.disabled || this.mode !== 'view')
5592
+ return;
5520
5593
  this.draftDescription = this.description;
5521
5594
  this.mode = 'editingDescription';
5595
+ this.cdr.markForCheck();
5522
5596
  setTimeout(() => this.focusEnd(this.descriptionInputRef), 0);
5523
5597
  }
5524
5598
  commitTitle() {
@@ -5544,6 +5618,18 @@ class EditableHeadingComponent {
5544
5618
  this.mode = 'view';
5545
5619
  this.descriptionChange.emit(value);
5546
5620
  }
5621
+ /**
5622
+ * Abandon the current edit and return to view mode. No change is emitted.
5623
+ * Wired to the Escape key on both edit inputs; also called internally
5624
+ * when `disabled` flips to true mid-edit.
5625
+ */
5626
+ cancel() {
5627
+ if (this.mode === 'view')
5628
+ return;
5629
+ this.draftTitle = this._title;
5630
+ this.draftDescription = this._description;
5631
+ this.mode = 'view';
5632
+ }
5547
5633
  // Listens on `mousedown` (not `click`) so the check runs before Angular
5548
5634
  // re-renders the trigger element out of the DOM — otherwise
5549
5635
  // `host.contains(target)` returns false for a click that landed on the
@@ -5561,23 +5647,24 @@ class EditableHeadingComponent {
5561
5647
  this.commitDescription();
5562
5648
  }
5563
5649
  }
5564
- // Focus with caret at end (rather than selecting all) so the user can
5565
- // continue typing where the previous value left off.
5650
+ // Position caret at end (rather than selecting all) so the user can continue
5651
+ // typing where the previous value left off. `riv-text-field` handles the
5652
+ // focus itself via `[autoFocus]`; we just fix up the selection range on the
5653
+ // descendant `<input>`.
5566
5654
  focusEnd(ref) {
5567
- const el = ref?.nativeElement;
5568
- if (!el)
5655
+ const input = ref?.nativeElement.querySelector('input');
5656
+ if (!input)
5569
5657
  return;
5570
- el.focus();
5571
- const len = el.value.length;
5572
- el.setSelectionRange(len, len);
5658
+ const len = input.value.length;
5659
+ input.setSelectionRange(len, len);
5573
5660
  this.cdr.markForCheck();
5574
5661
  }
5575
5662
  }
5576
5663
  EditableHeadingComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: EditableHeadingComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
5577
- EditableHeadingComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: EditableHeadingComponent, selector: "riv-editable-heading", inputs: { title: "title", description: "description", titleMaxLength: "titleMaxLength", descriptionMaxLength: "descriptionMaxLength" }, outputs: { titleChange: "titleChange", descriptionChange: "descriptionChange" }, host: { listeners: { "document:mousedown": "onDocumentMouseDown($event.target)" }, properties: { "class.editing": "this.isEditing" } }, viewQueries: [{ propertyName: "titleInputRef", first: true, predicate: ["titleInput"], descendants: true }, { propertyName: "descriptionInputRef", first: true, predicate: ["descriptionInput"], descendants: true }], ngImport: i0, template: "<div class=\"leading\">\n <ng-container [ngSwitch]=\"mode\">\n <!-- View mode -->\n <ng-container *ngSwitchDefault>\n <button\n type=\"button\"\n class=\"title\"\n [class.has-description]=\"description\"\n rivTruncate\n aria-label=\"Rename\"\n (click)=\"startEditingTitle()\"\n >\n {{ title || 'Untitled' }}\n </button>\n\n <div class=\"divider\"></div>\n\n <button\n *ngIf=\"description; else addDescription\"\n type=\"button\"\n class=\"description\"\n rivTruncate\n aria-label=\"Edit description\"\n (click)=\"startEditingDescription()\"\n >\n {{ description }}\n </button>\n\n <ng-template #addDescription>\n <div class=\"add-description-wrap\">\n <button\n rivButton\n [variant]=\"'ghost'\"\n [size]=\"'xsmall'\"\n [icon]=\"'Plus'\"\n [iconPosition]=\"'first'\"\n type=\"button\"\n (click)=\"startEditingDescription()\"\n >\n Add description\n </button>\n </div>\n </ng-template>\n </ng-container>\n\n <!-- Editing title -->\n <ng-container *ngSwitchCase=\"'editingTitle'\">\n <input\n #titleInput\n class=\"title-input\"\n type=\"text\"\n aria-label=\"Title\"\n [value]=\"draftTitle\"\n [attr.maxlength]=\"titleMaxLength\"\n (input)=\"draftTitle = titleInput.value\"\n (keydown.enter)=\"commitTitle()\"\n />\n <button\n rivButton\n type=\"button\"\n class=\"commit\"\n [variant]=\"'primary'\"\n [icon]=\"'Check'\"\n [disabled]=\"!draftTitle\"\n (click)=\"commitTitle()\"\n aria-label=\"Save title\"\n ></button>\n </ng-container>\n\n <!-- Editing description -->\n <ng-container *ngSwitchCase=\"'editingDescription'\">\n <input\n #descriptionInput\n class=\"description-input\"\n type=\"text\"\n aria-label=\"Description\"\n [value]=\"draftDescription\"\n [attr.maxlength]=\"descriptionMaxLength\"\n placeholder=\"Add a description\"\n (input)=\"draftDescription = descriptionInput.value\"\n (keydown.enter)=\"commitDescription()\"\n />\n <button\n rivButton\n type=\"button\"\n class=\"commit\"\n [variant]=\"'primary'\"\n [icon]=\"'Check'\"\n (click)=\"commitDescription()\"\n aria-label=\"Save description\"\n ></button>\n </ng-container>\n </ng-container>\n</div>\n\n<div class=\"actions\" *ngIf=\"!isEditing\">\n <ng-content select=\"[actions]\"></ng-content>\n</div>\n", styles: [":host{display:flex;align-items:stretch;gap:var(--size-large);min-height:calc(var(--base-grid-size) * 8);font:var(--body-medium)}.leading{flex:1 1 auto;min-width:0;display:flex;align-items:center;gap:var(--size-small)}.title,.description{padding:0;border:none;background:none;text-align:left;cursor:pointer;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.title{font:var(--stat-xsmall);line-height:calc(var(--base-grid-size) * 4.5);color:var(--type-light-high-contrast);max-width:100%}.title.has-description{width:-moz-fit-content;width:fit-content;max-width:50%;flex-shrink:0}.description{font:var(--input-small);line-height:calc(var(--base-grid-size) * 3.5);color:var(--type-light-low-contrast);flex:1 1 auto}.divider{align-self:stretch;border-right:var(--border-width) solid var(--border-light);margin:0 var(--base-grid-size) 0 0;flex-shrink:0}.add-description-wrap{flex-shrink:0}.title-input,.description-input{border:var(--border-width) solid var(--border-light);border-radius:var(--border-radius-small);padding:var(--size-small) var(--size-medium);color:var(--type-light-high-contrast);outline:none;min-width:0}.title-input{font:var(--stat-xsmall);flex:1 1 auto}.description-input{font:var(--input-small);flex:1 1 auto}.title-input:focus,.description-input:focus{border-color:var(--purp-60)}.commit{flex-shrink:0}.actions{display:flex;align-items:center;gap:var(--size-small);flex-shrink:0}\n"], dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i1.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "directive", type: i1.NgSwitchDefault, selector: "[ngSwitchDefault]" }, { kind: "component", type: ButtonComponent, selector: "[rivButton]", inputs: ["locked", "disabled", "loading", "full", "size", "variant", "icon", "iconPosition", "current"] }, { kind: "component", type: TruncateComponent, selector: "[rivTruncate]", inputs: ["maxTooltipWidth"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
5664
+ EditableHeadingComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: EditableHeadingComponent, selector: "riv-editable-heading", inputs: { title: "title", description: "description", titleMaxLength: "titleMaxLength", descriptionMaxLength: "descriptionMaxLength", disabled: "disabled" }, outputs: { titleChange: "titleChange", descriptionChange: "descriptionChange" }, host: { listeners: { "document:mousedown": "onDocumentMouseDown($event.target)" }, properties: { "class.editing": "this.isEditing" } }, viewQueries: [{ propertyName: "titleInputRef", first: true, predicate: ["titleInput"], descendants: true, read: ElementRef }, { propertyName: "descriptionInputRef", first: true, predicate: ["descriptionInput"], descendants: true, read: ElementRef }], ngImport: i0, template: "<div class=\"leading\">\n <ng-container [ngSwitch]=\"mode\">\n <!-- View mode -->\n <ng-container *ngSwitchDefault>\n <button\n type=\"button\"\n class=\"title\"\n [class.has-description]=\"description\"\n [disabled]=\"disabled\"\n rivTruncate\n aria-label=\"Rename\"\n (click)=\"startEditingTitle()\"\n >\n {{ title || 'Untitled' }}\n </button>\n\n <div class=\"divider\"></div>\n\n <button\n *ngIf=\"description; else addDescription\"\n type=\"button\"\n class=\"description\"\n [disabled]=\"disabled\"\n rivTruncate\n aria-label=\"Edit description\"\n (click)=\"startEditingDescription()\"\n >\n {{ description }}\n </button>\n\n <ng-template #addDescription>\n <div class=\"add-description-wrap\" *ngIf=\"!disabled\">\n <button\n rivButton\n [variant]=\"'ghost'\"\n [size]=\"'xsmall'\"\n [icon]=\"'Plus'\"\n [iconPosition]=\"'first'\"\n type=\"button\"\n (click)=\"startEditingDescription()\"\n >\n Add description\n </button>\n </div>\n </ng-template>\n </ng-container>\n\n <ng-container *ngSwitchCase=\"'editingTitle'\">\n <riv-text-field\n #titleInput\n class=\"title-input\"\n [value]=\"draftTitle\"\n [maxLength]=\"titleMaxLength\"\n [autoFocus]=\"true\"\n (valueChange)=\"draftTitle = $event\"\n (keydown.enter)=\"commitTitle()\"\n (keydown.escape)=\"cancel()\"\n ></riv-text-field>\n <button\n rivButton\n type=\"button\"\n class=\"commit\"\n [variant]=\"'primary'\"\n [icon]=\"'Check'\"\n [disabled]=\"!draftTitle\"\n (click)=\"commitTitle()\"\n aria-label=\"Save title\"\n ></button>\n </ng-container>\n\n <!-- Editing description -->\n <ng-container *ngSwitchCase=\"'editingDescription'\">\n <riv-text-field\n #descriptionInput\n class=\"description-input\"\n placeholder=\"Add a description\"\n [value]=\"draftDescription\"\n [maxLength]=\"descriptionMaxLength\"\n [autoFocus]=\"true\"\n (valueChange)=\"draftDescription = $event\"\n (keydown.enter)=\"commitDescription()\"\n (keydown.escape)=\"cancel()\"\n ></riv-text-field>\n <button\n rivButton\n type=\"button\"\n class=\"commit\"\n [variant]=\"'primary'\"\n [icon]=\"'Check'\"\n (click)=\"commitDescription()\"\n aria-label=\"Save description\"\n ></button>\n </ng-container>\n </ng-container>\n</div>\n\n<div class=\"actions\" *ngIf=\"!isEditing\">\n <ng-content select=\"[actions]\"></ng-content>\n</div>\n", styles: [":host{display:flex;align-items:stretch;gap:var(--size-large);min-height:calc(var(--base-grid-size) * 8);font:var(--body-medium)}.leading{flex:1 1 auto;min-width:0;display:flex;align-items:center;gap:var(--size-small)}.title,.description{padding:0;border:none;background:none;text-align:left;cursor:pointer;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.title:disabled,.description:disabled{cursor:default}.title{font:var(--stat-xsmall);line-height:calc(var(--base-grid-size) * 4.5);color:var(--type-light-high-contrast);max-width:100%}.title.has-description{width:-moz-fit-content;width:fit-content;max-width:50%;flex-shrink:0}.description{font:var(--input-small);line-height:calc(var(--base-grid-size) * 3.5);color:var(--type-light-low-contrast);flex:1 1 auto}.divider{align-self:stretch;border-right:var(--border-width) solid var(--border-light);margin:0 var(--base-grid-size) 0 0;flex-shrink:0}.add-description-wrap{flex-shrink:0}.title-input,.description-input{flex:1 1 auto;min-width:0}.commit{flex-shrink:0}.actions{display:flex;align-items:center;gap:var(--size-small);flex-shrink:0}\n"], dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i1.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "directive", type: i1.NgSwitchDefault, selector: "[ngSwitchDefault]" }, { kind: "component", type: ButtonComponent, selector: "[rivButton]", inputs: ["locked", "disabled", "loading", "full", "size", "variant", "icon", "iconPosition", "current"] }, { kind: "component", type: TextFieldComponent, selector: "riv-text-field", inputs: ["disabled", "readonly", "placeholder", "casing", "type", "size", "autoFocus", "name", "minLength", "maxLength", "value"], outputs: ["valueChange", "blur"] }, { kind: "component", type: TruncateComponent, selector: "[rivTruncate]", inputs: ["maxTooltipWidth"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
5578
5665
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: EditableHeadingComponent, decorators: [{
5579
5666
  type: Component,
5580
- args: [{ selector: 'riv-editable-heading', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"leading\">\n <ng-container [ngSwitch]=\"mode\">\n <!-- View mode -->\n <ng-container *ngSwitchDefault>\n <button\n type=\"button\"\n class=\"title\"\n [class.has-description]=\"description\"\n rivTruncate\n aria-label=\"Rename\"\n (click)=\"startEditingTitle()\"\n >\n {{ title || 'Untitled' }}\n </button>\n\n <div class=\"divider\"></div>\n\n <button\n *ngIf=\"description; else addDescription\"\n type=\"button\"\n class=\"description\"\n rivTruncate\n aria-label=\"Edit description\"\n (click)=\"startEditingDescription()\"\n >\n {{ description }}\n </button>\n\n <ng-template #addDescription>\n <div class=\"add-description-wrap\">\n <button\n rivButton\n [variant]=\"'ghost'\"\n [size]=\"'xsmall'\"\n [icon]=\"'Plus'\"\n [iconPosition]=\"'first'\"\n type=\"button\"\n (click)=\"startEditingDescription()\"\n >\n Add description\n </button>\n </div>\n </ng-template>\n </ng-container>\n\n <!-- Editing title -->\n <ng-container *ngSwitchCase=\"'editingTitle'\">\n <input\n #titleInput\n class=\"title-input\"\n type=\"text\"\n aria-label=\"Title\"\n [value]=\"draftTitle\"\n [attr.maxlength]=\"titleMaxLength\"\n (input)=\"draftTitle = titleInput.value\"\n (keydown.enter)=\"commitTitle()\"\n />\n <button\n rivButton\n type=\"button\"\n class=\"commit\"\n [variant]=\"'primary'\"\n [icon]=\"'Check'\"\n [disabled]=\"!draftTitle\"\n (click)=\"commitTitle()\"\n aria-label=\"Save title\"\n ></button>\n </ng-container>\n\n <!-- Editing description -->\n <ng-container *ngSwitchCase=\"'editingDescription'\">\n <input\n #descriptionInput\n class=\"description-input\"\n type=\"text\"\n aria-label=\"Description\"\n [value]=\"draftDescription\"\n [attr.maxlength]=\"descriptionMaxLength\"\n placeholder=\"Add a description\"\n (input)=\"draftDescription = descriptionInput.value\"\n (keydown.enter)=\"commitDescription()\"\n />\n <button\n rivButton\n type=\"button\"\n class=\"commit\"\n [variant]=\"'primary'\"\n [icon]=\"'Check'\"\n (click)=\"commitDescription()\"\n aria-label=\"Save description\"\n ></button>\n </ng-container>\n </ng-container>\n</div>\n\n<div class=\"actions\" *ngIf=\"!isEditing\">\n <ng-content select=\"[actions]\"></ng-content>\n</div>\n", styles: [":host{display:flex;align-items:stretch;gap:var(--size-large);min-height:calc(var(--base-grid-size) * 8);font:var(--body-medium)}.leading{flex:1 1 auto;min-width:0;display:flex;align-items:center;gap:var(--size-small)}.title,.description{padding:0;border:none;background:none;text-align:left;cursor:pointer;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.title{font:var(--stat-xsmall);line-height:calc(var(--base-grid-size) * 4.5);color:var(--type-light-high-contrast);max-width:100%}.title.has-description{width:-moz-fit-content;width:fit-content;max-width:50%;flex-shrink:0}.description{font:var(--input-small);line-height:calc(var(--base-grid-size) * 3.5);color:var(--type-light-low-contrast);flex:1 1 auto}.divider{align-self:stretch;border-right:var(--border-width) solid var(--border-light);margin:0 var(--base-grid-size) 0 0;flex-shrink:0}.add-description-wrap{flex-shrink:0}.title-input,.description-input{border:var(--border-width) solid var(--border-light);border-radius:var(--border-radius-small);padding:var(--size-small) var(--size-medium);color:var(--type-light-high-contrast);outline:none;min-width:0}.title-input{font:var(--stat-xsmall);flex:1 1 auto}.description-input{font:var(--input-small);flex:1 1 auto}.title-input:focus,.description-input:focus{border-color:var(--purp-60)}.commit{flex-shrink:0}.actions{display:flex;align-items:center;gap:var(--size-small);flex-shrink:0}\n"] }]
5667
+ args: [{ selector: 'riv-editable-heading', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"leading\">\n <ng-container [ngSwitch]=\"mode\">\n <!-- View mode -->\n <ng-container *ngSwitchDefault>\n <button\n type=\"button\"\n class=\"title\"\n [class.has-description]=\"description\"\n [disabled]=\"disabled\"\n rivTruncate\n aria-label=\"Rename\"\n (click)=\"startEditingTitle()\"\n >\n {{ title || 'Untitled' }}\n </button>\n\n <div class=\"divider\"></div>\n\n <button\n *ngIf=\"description; else addDescription\"\n type=\"button\"\n class=\"description\"\n [disabled]=\"disabled\"\n rivTruncate\n aria-label=\"Edit description\"\n (click)=\"startEditingDescription()\"\n >\n {{ description }}\n </button>\n\n <ng-template #addDescription>\n <div class=\"add-description-wrap\" *ngIf=\"!disabled\">\n <button\n rivButton\n [variant]=\"'ghost'\"\n [size]=\"'xsmall'\"\n [icon]=\"'Plus'\"\n [iconPosition]=\"'first'\"\n type=\"button\"\n (click)=\"startEditingDescription()\"\n >\n Add description\n </button>\n </div>\n </ng-template>\n </ng-container>\n\n <ng-container *ngSwitchCase=\"'editingTitle'\">\n <riv-text-field\n #titleInput\n class=\"title-input\"\n [value]=\"draftTitle\"\n [maxLength]=\"titleMaxLength\"\n [autoFocus]=\"true\"\n (valueChange)=\"draftTitle = $event\"\n (keydown.enter)=\"commitTitle()\"\n (keydown.escape)=\"cancel()\"\n ></riv-text-field>\n <button\n rivButton\n type=\"button\"\n class=\"commit\"\n [variant]=\"'primary'\"\n [icon]=\"'Check'\"\n [disabled]=\"!draftTitle\"\n (click)=\"commitTitle()\"\n aria-label=\"Save title\"\n ></button>\n </ng-container>\n\n <!-- Editing description -->\n <ng-container *ngSwitchCase=\"'editingDescription'\">\n <riv-text-field\n #descriptionInput\n class=\"description-input\"\n placeholder=\"Add a description\"\n [value]=\"draftDescription\"\n [maxLength]=\"descriptionMaxLength\"\n [autoFocus]=\"true\"\n (valueChange)=\"draftDescription = $event\"\n (keydown.enter)=\"commitDescription()\"\n (keydown.escape)=\"cancel()\"\n ></riv-text-field>\n <button\n rivButton\n type=\"button\"\n class=\"commit\"\n [variant]=\"'primary'\"\n [icon]=\"'Check'\"\n (click)=\"commitDescription()\"\n aria-label=\"Save description\"\n ></button>\n </ng-container>\n </ng-container>\n</div>\n\n<div class=\"actions\" *ngIf=\"!isEditing\">\n <ng-content select=\"[actions]\"></ng-content>\n</div>\n", styles: [":host{display:flex;align-items:stretch;gap:var(--size-large);min-height:calc(var(--base-grid-size) * 8);font:var(--body-medium)}.leading{flex:1 1 auto;min-width:0;display:flex;align-items:center;gap:var(--size-small)}.title,.description{padding:0;border:none;background:none;text-align:left;cursor:pointer;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.title:disabled,.description:disabled{cursor:default}.title{font:var(--stat-xsmall);line-height:calc(var(--base-grid-size) * 4.5);color:var(--type-light-high-contrast);max-width:100%}.title.has-description{width:-moz-fit-content;width:fit-content;max-width:50%;flex-shrink:0}.description{font:var(--input-small);line-height:calc(var(--base-grid-size) * 3.5);color:var(--type-light-low-contrast);flex:1 1 auto}.divider{align-self:stretch;border-right:var(--border-width) solid var(--border-light);margin:0 var(--base-grid-size) 0 0;flex-shrink:0}.add-description-wrap{flex-shrink:0}.title-input,.description-input{flex:1 1 auto;min-width:0}.commit{flex-shrink:0}.actions{display:flex;align-items:center;gap:var(--size-small);flex-shrink:0}\n"] }]
5581
5668
  }], ctorParameters: function () { return [{ type: i0.ChangeDetectorRef }, { type: i0.ElementRef }]; }, propDecorators: { title: [{
5582
5669
  type: Input
5583
5670
  }], description: [{
@@ -5586,16 +5673,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImpo
5586
5673
  type: Input
5587
5674
  }], descriptionMaxLength: [{
5588
5675
  type: Input
5676
+ }], disabled: [{
5677
+ type: Input
5589
5678
  }], titleChange: [{
5590
5679
  type: Output
5591
5680
  }], descriptionChange: [{
5592
5681
  type: Output
5593
5682
  }], titleInputRef: [{
5594
5683
  type: ViewChild,
5595
- args: ['titleInput']
5684
+ args: ['titleInput', { read: ElementRef }]
5596
5685
  }], descriptionInputRef: [{
5597
5686
  type: ViewChild,
5598
- args: ['descriptionInput']
5687
+ args: ['descriptionInput', { read: ElementRef }]
5599
5688
  }], isEditing: [{
5600
5689
  type: HostBinding,
5601
5690
  args: ['class.editing']