@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.
@@ -4221,18 +4221,20 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImpo
4221
4221
  'large',
4222
4222
  'xlarge',
4223
4223
  ];
4224
- // Accepts MM/DD/YYYY, M/D/YY, and YYYY-MM-DD with /, -, or . separators.
4225
- // A 4-digit leading segment is treated as year.
4224
+ // Accepts MM/DD/YYYY, M/D/YY, and YYYY-MM-DD with /, -, or . separators, as
4225
+ // well as separator-less digit runs (MMDDYYYY or MMDDYY). A 4-digit leading
4226
+ // segment in the separated form is treated as an ISO year; 2-digit years
4227
+ // pivot at 50 (<50 → 2000s, >=50 → 1900s).
4226
4228
  function parseDate(input) {
4227
- const parts = input.trim().split(/[/.-]/);
4228
- if (parts.length !== 3)
4229
+ const trimmed = input.trim();
4230
+ if (!trimmed)
4229
4231
  return undefined;
4230
- const nums = parts.map(p => parseInt(p, 10));
4231
- if (nums.some(isNaN))
4232
+ const ymd = /^\d+$/.test(trimmed)
4233
+ ? parseDigits(trimmed)
4234
+ : parseSeparated(trimmed);
4235
+ if (!ymd)
4232
4236
  return undefined;
4233
- let [year, month, day] = parts[0].length === 4 ? nums : [nums[2], nums[0], nums[1]];
4234
- if (parts[0].length !== 4 && year < 100)
4235
- year += year < 50 ? 2000 : 1900;
4237
+ const [year, month, day] = ymd;
4236
4238
  const date = new Date(year, month - 1, day);
4237
4239
  return date.getFullYear() === year &&
4238
4240
  date.getMonth() === month - 1 &&
@@ -4241,6 +4243,35 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImpo
4241
4243
  : undefined;
4242
4244
  }
4243
4245
  DateComponent.parseDate = parseDate;
4246
+ function expandTwoDigitYear(year) {
4247
+ return year < 100 ? year + (year < 50 ? 2000 : 1900) : year;
4248
+ }
4249
+ // A run of digits with no separators: 8 → MMDDYYYY (literal year), 6 → MMDDYY
4250
+ // (2-digit year pivoted at 50).
4251
+ function parseDigits(digits) {
4252
+ if (digits.length !== 8 && digits.length !== 6)
4253
+ return undefined;
4254
+ const year = parseInt(digits.slice(4), 10);
4255
+ return [
4256
+ digits.length === 6 ? expandTwoDigitYear(year) : year,
4257
+ parseInt(digits.slice(0, 2), 10),
4258
+ parseInt(digits.slice(2, 4), 10),
4259
+ ];
4260
+ }
4261
+ function parseSeparated(input) {
4262
+ const parts = input.split(/[/.-]/);
4263
+ if (parts.length !== 3)
4264
+ return undefined;
4265
+ const nums = parts.map(p => parseInt(p, 10));
4266
+ if (nums.some(isNaN))
4267
+ return undefined;
4268
+ const [year, month, day] = parts[0].length === 4 ? nums : [nums[2], nums[0], nums[1]];
4269
+ return [
4270
+ parts[0].length === 4 ? year : expandTwoDigitYear(year),
4271
+ month,
4272
+ day,
4273
+ ];
4274
+ }
4244
4275
  function isValidDate(date, min, max) {
4245
4276
  const time = date.getTime();
4246
4277
  return time >= min.getTime() && time <= max.getTime();
@@ -5364,12 +5395,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImpo
5364
5395
  * "commit" framing over "change" is deliberate: consumers that persist on
5365
5396
  * commit (analytics, PATCH) treat the gesture itself as the event; those
5366
5397
  * that only care about diffs can compare `next === current` in the handler.
5367
- * There is no cancel path.
5368
5398
  *
5369
- * Outside-click commits (rather than cancels) to match the established DS
5370
- * interaction pattern in `riv-date` and `riv-select`. If a future consumer
5371
- * needs an abandon path, the soft first step is Escape-to-cancel rather
5372
- * than changing outside-click's meaning.
5399
+ * Escape cancels: reverts the draft back to the current value and drops out
5400
+ * of edit mode WITHOUT emitting a change. Outside-click still commits (to
5401
+ * match `riv-date` / `riv-select`); Escape is the deliberate abandon path.
5373
5402
  *
5374
5403
  * Value handling follows the same convention as `riv-text-field`: `[title]`
5375
5404
  * and `[description]` are treated as suggestions, not authoritative state.
@@ -5378,6 +5407,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImpo
5378
5407
  * a different value. That lets the component demo out of the box (e.g. in
5379
5408
  * Storybook) without requiring a round trip through the parent.
5380
5409
  *
5410
+ * Programmatic entry into edit mode is available via `startEditingTitle()`
5411
+ * and `startEditingDescription()` — obtain a reference with
5412
+ * `@ViewChild(EditableHeadingComponent)`. Use these for secondary entry
5413
+ * points like a kebab-menu "Rename" item, or to auto-open rename after
5414
+ * creating a new entity.
5415
+ *
5381
5416
  * The component owns no border, background, or card chrome — consumers who
5382
5417
  * want a bordered container should wrap it.
5383
5418
  *
@@ -5414,6 +5449,15 @@ class EditableHeadingComponent {
5414
5449
  get description() {
5415
5450
  return this._description;
5416
5451
  }
5452
+ set disabled(v) {
5453
+ this._disabled = v;
5454
+ if (v && this.mode !== 'view')
5455
+ this.cancel();
5456
+ this.cdr.markForCheck();
5457
+ }
5458
+ get disabled() {
5459
+ return this._disabled;
5460
+ }
5417
5461
  constructor(cdr, host) {
5418
5462
  this.cdr = cdr;
5419
5463
  this.host = host;
@@ -5422,6 +5466,13 @@ class EditableHeadingComponent {
5422
5466
  this._description = '';
5423
5467
  this.titleMaxLength = 100;
5424
5468
  this.descriptionMaxLength = 300;
5469
+ /**
5470
+ * When true, the title and description are not clickable and cannot be
5471
+ * edited. Any in-progress edit is dropped without emitting. Imperative
5472
+ * `startEditing*` calls no-op. Useful for consumers that need to gate
5473
+ * editing on a permission or a broader "actions disabled" state.
5474
+ */
5475
+ this._disabled = false;
5425
5476
  this.titleChange = new EventEmitter();
5426
5477
  this.descriptionChange = new EventEmitter();
5427
5478
  this.mode = 'view';
@@ -5431,15 +5482,38 @@ class EditableHeadingComponent {
5431
5482
  get isEditing() {
5432
5483
  return this.mode !== 'view';
5433
5484
  }
5485
+ /**
5486
+ * Enter title-edit mode programmatically. Same behavior as clicking the
5487
+ * title — populates the edit buffer from the current value, focuses the
5488
+ * input with the caret at the end, and hides the actions slot. No-op if
5489
+ * already in an editing mode or when the component is disabled.
5490
+ *
5491
+ * Intended for consumers that offer a secondary entry point to renaming
5492
+ * (e.g. a kebab menu item), or that want to auto-open rename after
5493
+ * creating a new entity. Bind via `@ViewChild(EditableHeadingComponent)`.
5494
+ */
5434
5495
  startEditingTitle() {
5496
+ if (this.disabled || this.mode !== 'view')
5497
+ return;
5435
5498
  this.draftTitle = this.title;
5436
5499
  this.mode = 'editingTitle';
5500
+ // `markForCheck` so consumers can call this from outside Angular's zone
5501
+ // (e.g. an async callback) without having to trigger CD themselves.
5502
+ this.cdr.markForCheck();
5437
5503
  // Defer focus until Angular has rendered the input into the DOM.
5438
5504
  setTimeout(() => this.focusEnd(this.titleInputRef), 0);
5439
5505
  }
5506
+ /**
5507
+ * Enter description-edit mode programmatically. Same behavior as clicking
5508
+ * the description (or the "+ Add description" button). No-op if already
5509
+ * in an editing mode or when the component is disabled.
5510
+ */
5440
5511
  startEditingDescription() {
5512
+ if (this.disabled || this.mode !== 'view')
5513
+ return;
5441
5514
  this.draftDescription = this.description;
5442
5515
  this.mode = 'editingDescription';
5516
+ this.cdr.markForCheck();
5443
5517
  setTimeout(() => this.focusEnd(this.descriptionInputRef), 0);
5444
5518
  }
5445
5519
  commitTitle() {
@@ -5465,6 +5539,18 @@ class EditableHeadingComponent {
5465
5539
  this.mode = 'view';
5466
5540
  this.descriptionChange.emit(value);
5467
5541
  }
5542
+ /**
5543
+ * Abandon the current edit and return to view mode. No change is emitted.
5544
+ * Wired to the Escape key on both edit inputs; also called internally
5545
+ * when `disabled` flips to true mid-edit.
5546
+ */
5547
+ cancel() {
5548
+ if (this.mode === 'view')
5549
+ return;
5550
+ this.draftTitle = this._title;
5551
+ this.draftDescription = this._description;
5552
+ this.mode = 'view';
5553
+ }
5468
5554
  // Listens on `mousedown` (not `click`) so the check runs before Angular
5469
5555
  // re-renders the trigger element out of the DOM — otherwise
5470
5556
  // `host.contains(target)` returns false for a click that landed on the
@@ -5482,23 +5568,24 @@ class EditableHeadingComponent {
5482
5568
  this.commitDescription();
5483
5569
  }
5484
5570
  }
5485
- // Focus with caret at end (rather than selecting all) so the user can
5486
- // continue typing where the previous value left off.
5571
+ // Position caret at end (rather than selecting all) so the user can continue
5572
+ // typing where the previous value left off. `riv-text-field` handles the
5573
+ // focus itself via `[autoFocus]`; we just fix up the selection range on the
5574
+ // descendant `<input>`.
5487
5575
  focusEnd(ref) {
5488
- const el = ref === null || ref === void 0 ? void 0 : ref.nativeElement;
5489
- if (!el)
5576
+ const input = ref === null || ref === void 0 ? void 0 : ref.nativeElement.querySelector('input');
5577
+ if (!input)
5490
5578
  return;
5491
- el.focus();
5492
- const len = el.value.length;
5493
- el.setSelectionRange(len, len);
5579
+ const len = input.value.length;
5580
+ input.setSelectionRange(len, len);
5494
5581
  this.cdr.markForCheck();
5495
5582
  }
5496
5583
  }
5497
5584
  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 });
5498
- 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 });
5585
+ 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 });
5499
5586
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: EditableHeadingComponent, decorators: [{
5500
5587
  type: Component,
5501
- 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"] }]
5588
+ 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"] }]
5502
5589
  }], ctorParameters: function () { return [{ type: i0.ChangeDetectorRef }, { type: i0.ElementRef }]; }, propDecorators: { title: [{
5503
5590
  type: Input
5504
5591
  }], description: [{
@@ -5507,16 +5594,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImpo
5507
5594
  type: Input
5508
5595
  }], descriptionMaxLength: [{
5509
5596
  type: Input
5597
+ }], disabled: [{
5598
+ type: Input
5510
5599
  }], titleChange: [{
5511
5600
  type: Output
5512
5601
  }], descriptionChange: [{
5513
5602
  type: Output
5514
5603
  }], titleInputRef: [{
5515
5604
  type: ViewChild,
5516
- args: ['titleInput']
5605
+ args: ['titleInput', { read: ElementRef }]
5517
5606
  }], descriptionInputRef: [{
5518
5607
  type: ViewChild,
5519
- args: ['descriptionInput']
5608
+ args: ['descriptionInput', { read: ElementRef }]
5520
5609
  }], isEditing: [{
5521
5610
  type: HostBinding,
5522
5611
  args: ['class.editing']