@ni/nimble-components 20.14.8 → 20.14.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/dist/all-components-bundle.js +386 -136
  2. package/dist/all-components-bundle.js.map +1 -1
  3. package/dist/all-components-bundle.min.js +2496 -2464
  4. package/dist/all-components-bundle.min.js.map +1 -1
  5. package/dist/esm/combobox/index.d.ts +2 -0
  6. package/dist/esm/combobox/index.js +5 -0
  7. package/dist/esm/combobox/index.js.map +1 -1
  8. package/dist/esm/combobox/template.js +3 -0
  9. package/dist/esm/combobox/template.js.map +1 -1
  10. package/dist/esm/list-option/index.d.ts +6 -3
  11. package/dist/esm/list-option/index.js +17 -14
  12. package/dist/esm/list-option/index.js.map +1 -1
  13. package/dist/esm/list-option/styles.js +3 -0
  14. package/dist/esm/list-option/styles.js.map +1 -1
  15. package/dist/esm/list-option/template.d.ts +8 -0
  16. package/dist/esm/list-option/template.js +34 -0
  17. package/dist/esm/list-option/template.js.map +1 -0
  18. package/dist/esm/rich-text/models/markdown-parser-mention-configuration.d.ts +15 -0
  19. package/dist/esm/rich-text/models/markdown-parser-mention-configuration.js +26 -0
  20. package/dist/esm/rich-text/models/markdown-parser-mention-configuration.js.map +1 -0
  21. package/dist/esm/rich-text/models/markdown-parser.d.ts +9 -2
  22. package/dist/esm/rich-text/models/markdown-parser.js +34 -9
  23. package/dist/esm/rich-text/models/markdown-parser.js.map +1 -1
  24. package/dist/esm/rich-text/models/testing/markdown-parser-utils.d.ts +1 -0
  25. package/dist/esm/rich-text/models/testing/markdown-parser-utils.js +3 -0
  26. package/dist/esm/rich-text/models/testing/markdown-parser-utils.js.map +1 -1
  27. package/dist/esm/rich-text/viewer/index.d.ts +24 -0
  28. package/dist/esm/rich-text/viewer/index.js +66 -4
  29. package/dist/esm/rich-text/viewer/index.js.map +1 -1
  30. package/dist/esm/rich-text/viewer/styles.js +14 -1
  31. package/dist/esm/rich-text/viewer/styles.js.map +1 -1
  32. package/dist/esm/rich-text/viewer/template.js +4 -2
  33. package/dist/esm/rich-text/viewer/template.js.map +1 -1
  34. package/dist/esm/rich-text/viewer/testing/rich-text-viewer.pageobject.d.ts +1 -0
  35. package/dist/esm/rich-text/viewer/testing/rich-text-viewer.pageobject.js +6 -3
  36. package/dist/esm/rich-text/viewer/testing/rich-text-viewer.pageobject.js.map +1 -1
  37. package/dist/esm/rich-text-mention/base/models/mention-internals.d.ts +5 -0
  38. package/dist/esm/rich-text-mention/base/models/mention-internals.js +1 -0
  39. package/dist/esm/rich-text-mention/base/models/mention-internals.js.map +1 -1
  40. package/dist/esm/rich-text-mention/users/index.js +3 -1
  41. package/dist/esm/rich-text-mention/users/index.js.map +1 -1
  42. package/dist/esm/select/index.d.ts +2 -0
  43. package/dist/esm/select/index.js +5 -0
  44. package/dist/esm/select/index.js.map +1 -1
  45. package/dist/esm/select/template.js +2 -1
  46. package/dist/esm/select/template.js.map +1 -1
  47. package/package.json +1 -1
@@ -10270,30 +10270,6 @@
10270
10270
  return isNodeMatchingSelectorFocusable(options, node);
10271
10271
  };
10272
10272
 
10273
- /**
10274
- * The template for the {@link @microsoft/fast-foundation#(ListboxOption:class)} component.
10275
- * @public
10276
- */
10277
- const listboxOptionTemplate = (context, definition) => html `
10278
- <template
10279
- aria-checked="${x => x.ariaChecked}"
10280
- aria-disabled="${x => x.ariaDisabled}"
10281
- aria-posinset="${x => x.ariaPosInSet}"
10282
- aria-selected="${x => x.ariaSelected}"
10283
- aria-setsize="${x => x.ariaSetSize}"
10284
- class="${x => [x.checked && "checked", x.selected && "selected", x.disabled && "disabled"]
10285
- .filter(Boolean)
10286
- .join(" ")}"
10287
- role="option"
10288
- >
10289
- ${startSlotTemplate(context, definition)}
10290
- <span class="content" part="content">
10291
- <slot ${slotted("content")}></slot>
10292
- </span>
10293
- ${endSlotTemplate(context, definition)}
10294
- </template>
10295
- `;
10296
-
10297
10273
  /**
10298
10274
  * A Listbox Custom HTML Element.
10299
10275
  * Implements the {@link https://w3c.github.io/aria/#listbox | ARIA listbox }.
@@ -16298,7 +16274,7 @@
16298
16274
 
16299
16275
  /**
16300
16276
  * Do not edit directly
16301
- * Generated on Tue, 21 Nov 2023 19:01:43 GMT
16277
+ * Generated on Mon, 27 Nov 2023 15:57:15 GMT
16302
16278
  */
16303
16279
 
16304
16280
  const Information100DarkUi = "#a46eff";
@@ -16698,7 +16674,7 @@
16698
16674
  return `${prefix}${uniqueIdCounter++}`;
16699
16675
  }
16700
16676
 
16701
- const template$B = html `<slot></slot>`;
16677
+ const template$C = html `<slot></slot>`;
16702
16678
 
16703
16679
  const styles$U = css `
16704
16680
  :host {
@@ -16818,7 +16794,7 @@
16818
16794
  const nimbleDesignSystemProvider = ThemeProvider.compose({
16819
16795
  baseName: 'theme-provider',
16820
16796
  styles: styles$U,
16821
- template: template$B
16797
+ template: template$C
16822
16798
  });
16823
16799
  DesignSystem.getOrCreate()
16824
16800
  .withPrefix('nimble')
@@ -17104,7 +17080,7 @@
17104
17080
  `;
17105
17081
 
17106
17082
  // prettier-ignore
17107
- const template$A = (_context, definition) => html `<a
17083
+ const template$B = (_context, definition) => html `<a
17108
17084
  class="control"
17109
17085
  part="control"
17110
17086
  download="${x => x.download}"
@@ -17194,7 +17170,7 @@
17194
17170
  const nimbleAnchor = Anchor.compose({
17195
17171
  baseName: 'anchor',
17196
17172
  baseClass: Anchor$1,
17197
- template: template$A,
17173
+ template: template$B,
17198
17174
  styles: styles$T,
17199
17175
  shadowOptions: {
17200
17176
  delegatesFocus: true
@@ -17584,7 +17560,7 @@
17584
17560
  }
17585
17561
  `;
17586
17562
 
17587
- const template$z = (context, definition) => html `
17563
+ const template$A = (context, definition) => html `
17588
17564
  <a
17589
17565
  class="control"
17590
17566
  part="control"
@@ -17666,7 +17642,7 @@
17666
17642
  ], AnchorButton.prototype, "disabled", void 0);
17667
17643
  const nimbleAnchorButton = AnchorButton.compose({
17668
17644
  baseName: 'anchor-button',
17669
- template: template$z,
17645
+ template: template$A,
17670
17646
  styles: styles$R,
17671
17647
  shadowOptions: {
17672
17648
  delegatesFocus: true
@@ -17752,7 +17728,7 @@
17752
17728
  }
17753
17729
  `;
17754
17730
 
17755
- const template$y = (context, definition) => html `
17731
+ const template$z = (context, definition) => html `
17756
17732
  <template
17757
17733
  role="menuitem"
17758
17734
  class="${x => (typeof x.startColumnCount === 'number'
@@ -17858,7 +17834,7 @@
17858
17834
  // FoundationAnchor already applies the StartEnd mixin, so we don't need to do it here.
17859
17835
  const nimbleAnchorMenuItem = AnchorMenuItem.compose({
17860
17836
  baseName: 'anchor-menu-item',
17861
- template: template$y,
17837
+ template: template$z,
17862
17838
  styles: styles$Q,
17863
17839
  shadowOptions: {
17864
17840
  delegatesFocus: true
@@ -17999,7 +17975,7 @@
17999
17975
  }
18000
17976
  `;
18001
17977
 
18002
- const template$x = (context, definition) => html `
17978
+ const template$y = (context, definition) => html `
18003
17979
  <template slot="anchortab" role="tab" aria-disabled="${x => x.disabled}">
18004
17980
  <a
18005
17981
  download="${x => x.download}"
@@ -18051,7 +18027,7 @@
18051
18027
  // FoundationAnchor already applies the StartEnd mixin, so we don't need to do it here.
18052
18028
  const nimbleAnchorTab = AnchorTab.compose({
18053
18029
  baseName: 'anchor-tab',
18054
- template: template$x,
18030
+ template: template$y,
18055
18031
  styles: styles$P,
18056
18032
  shadowOptions: {
18057
18033
  delegatesFocus: true
@@ -18082,7 +18058,7 @@
18082
18058
  }
18083
18059
  `;
18084
18060
 
18085
- const template$w = (context, definition) => html `
18061
+ const template$x = (context, definition) => html `
18086
18062
  ${startSlotTemplate(context, definition)}
18087
18063
  <div ${ref('tablist')} class="tablist" part="tablist" role="tablist">
18088
18064
  <slot name="anchortab" ${slotted('tabs')}></slot>
@@ -18288,7 +18264,7 @@
18288
18264
  applyMixins(AnchorTabs, StartEnd);
18289
18265
  const nimbleAnchorTabs = AnchorTabs.compose({
18290
18266
  baseName: 'anchor-tabs',
18291
- template: template$w,
18267
+ template: template$x,
18292
18268
  styles: styles$O,
18293
18269
  shadowOptions: {
18294
18270
  delegatesFocus: false
@@ -18407,7 +18383,7 @@
18407
18383
  }
18408
18384
  `;
18409
18385
 
18410
- const template$v = (context, definition) => html `
18386
+ const template$w = (context, definition) => html `
18411
18387
  <template
18412
18388
  role="treeitem"
18413
18389
  slot="${x => (x.isNestedItem() ? 'item' : null)}"
@@ -18544,7 +18520,7 @@
18544
18520
  // FoundationAnchor already applies the StartEnd mixin, so we don't need to do it here.
18545
18521
  const nimbleAnchorTreeItem = AnchorTreeItem.compose({
18546
18522
  baseName: 'anchor-tree-item',
18547
- template: template$v,
18523
+ template: template$w,
18548
18524
  styles: styles$N,
18549
18525
  shadowOptions: {
18550
18526
  delegatesFocus: true
@@ -19540,7 +19516,7 @@
19540
19516
  };
19541
19517
 
19542
19518
  // Avoiding any whitespace in the template because this is an inline element
19543
- const template$u = html `<div
19519
+ const template$v = html `<div
19544
19520
  class="icon"
19545
19521
  aria-hidden="true"
19546
19522
  :innerHTML=${x => x.icon.data}
@@ -19599,7 +19575,7 @@
19599
19575
  const registerIcon = (baseName, iconClass) => {
19600
19576
  const composedIcon = iconClass.compose({
19601
19577
  baseName,
19602
- template: template$u,
19578
+ template: template$v,
19603
19579
  styles: styles$J,
19604
19580
  baseClass: iconClass
19605
19581
  });
@@ -19704,7 +19680,7 @@
19704
19680
  }).withDefault(coreLabelDefaults.informationIconLabel);
19705
19681
 
19706
19682
  // prettier-ignore
19707
- const template$t = html `
19683
+ const template$u = html `
19708
19684
  <div class="container"
19709
19685
  role="status"
19710
19686
  aria-atomic="${x => x.ariaAtomic}"
@@ -19820,7 +19796,7 @@
19820
19796
  applyMixins(Banner, ARIAGlobalStatesAndProperties);
19821
19797
  const nimbleBanner = Banner.compose({
19822
19798
  baseName: 'banner',
19823
- template: template$t,
19799
+ template: template$u,
19824
19800
  styles: styles$L
19825
19801
  });
19826
19802
  DesignSystem.getOrCreate().withPrefix('nimble').register(nimbleBanner());
@@ -19977,7 +19953,7 @@
19977
19953
  }
19978
19954
  `;
19979
19955
 
19980
- const template$s = html `
19956
+ const template$t = html `
19981
19957
  ${'' /* Explicitly set role to work around Lighthouse error. See https://github.com/ni/nimble/issues/1650. */}
19982
19958
  <section role="region" aria-labelledby="title-slot">
19983
19959
  <slot name="title" id="title-slot"></slot>
@@ -19993,7 +19969,7 @@
19993
19969
  const nimbleCard = Card.compose({
19994
19970
  baseName: 'card',
19995
19971
  baseClass: Card$1,
19996
- template: template$s,
19972
+ template: template$t,
19997
19973
  styles: styles$G
19998
19974
  });
19999
19975
  DesignSystem.getOrCreate().withPrefix('nimble').register(nimbleCard());
@@ -20371,7 +20347,7 @@
20371
20347
  }
20372
20348
  `;
20373
20349
 
20374
- const template$r = (context, definition) => html `
20350
+ const template$s = (context, definition) => html `
20375
20351
  <div
20376
20352
  role="button"
20377
20353
  part="control"
@@ -20446,7 +20422,7 @@
20446
20422
  applyMixins(ToggleButton, StartEnd, DelegatesARIAButton);
20447
20423
  const nimbleToggleButton = ToggleButton.compose({
20448
20424
  baseName: 'toggle-button',
20449
- template: template$r,
20425
+ template: template$s,
20450
20426
  styles: styles$D,
20451
20427
  shadowOptions: {
20452
20428
  delegatesFocus: true
@@ -20826,8 +20802,63 @@
20826
20802
  }
20827
20803
  `));
20828
20804
 
20805
+ /**
20806
+ * The runtime behavior for template overflow detection.
20807
+ * @public
20808
+ */
20809
+ class OverflowBehavior {
20810
+ /**
20811
+ * Creates an instance of OverflowBehavior.
20812
+ * @param target - The element to check for overflow.
20813
+ * @param propertyName - The name of the property to assign the overflow state to.
20814
+ */
20815
+ constructor(target, propertyName) {
20816
+ this.target = target;
20817
+ this.propertyName = propertyName;
20818
+ }
20819
+ /**
20820
+ * Bind this behavior to the source.
20821
+ * @param source - The source to bind to.
20822
+ * @param context - The execution context that the binding is operating within.
20823
+ */
20824
+ bind(source) {
20825
+ this.source = source;
20826
+ this.setSourceValue(false);
20827
+ this.mouseOverHandler = () => {
20828
+ const hasOverflow = this.target.offsetWidth < this.target.scrollWidth;
20829
+ this.setSourceValue(hasOverflow);
20830
+ };
20831
+ this.mouseOutHandler = () => {
20832
+ this.setSourceValue(false);
20833
+ };
20834
+ this.target.addEventListener('mouseover', this.mouseOverHandler);
20835
+ this.target.addEventListener('mouseout', this.mouseOutHandler);
20836
+ }
20837
+ /**
20838
+ * Unbinds this behavior from the source.
20839
+ * @param source - The source to unbind from.
20840
+ */
20841
+ unbind() {
20842
+ this.source = undefined;
20843
+ this.target.removeEventListener('mouseover', this.mouseOverHandler);
20844
+ this.target.removeEventListener('mouseout', this.mouseOutHandler);
20845
+ }
20846
+ setSourceValue(value) {
20847
+ // @ts-expect-error set property on source
20848
+ this.source[this.propertyName] = value;
20849
+ }
20850
+ }
20851
+ /**
20852
+ * A directive that observes if an element has overflow and sets a flag.
20853
+ * @param propertyName - The name of the property to assign the overflow flag.
20854
+ * @public
20855
+ */
20856
+ function overflow(propertyName) {
20857
+ return new AttachedBehaviorHTMLDirective('nimble-overflow', OverflowBehavior, propertyName);
20858
+ }
20859
+
20829
20860
  // prettier-ignore
20830
- const template$q = (context, definition) => html `
20861
+ const template$r = (context, definition) => html `
20831
20862
  <template
20832
20863
  aria-disabled="${x => x.ariaDisabled}"
20833
20864
  autocomplete="${x => x.autocomplete}"
@@ -20858,6 +20889,8 @@
20858
20889
  @input="${(x, c) => x.inputHandler(c.event)}"
20859
20890
  @keyup="${(x, c) => x.keyupHandler(c.event)}"
20860
20891
  ${ref('control')}
20892
+ ${overflow('hasOverflow')}
20893
+ title=${x => (x.hasOverflow && x.value ? x.value : null)}
20861
20894
  />
20862
20895
  <div class="indicator" part="indicator" aria-hidden="true">
20863
20896
  <slot name="indicator">
@@ -20906,6 +20939,8 @@
20906
20939
  super(...arguments);
20907
20940
  this.appearance = DropdownAppearance.underline;
20908
20941
  this.errorVisible = false;
20942
+ /** @internal */
20943
+ this.hasOverflow = false;
20909
20944
  this.valueUpdatedByInput = false;
20910
20945
  }
20911
20946
  get value() {
@@ -21095,10 +21130,13 @@
21095
21130
  __decorate$1([
21096
21131
  observable
21097
21132
  ], Combobox.prototype, "controlWrapper", void 0);
21133
+ __decorate$1([
21134
+ observable
21135
+ ], Combobox.prototype, "hasOverflow", void 0);
21098
21136
  const nimbleCombobox = Combobox.compose({
21099
21137
  baseName: 'combobox',
21100
21138
  baseClass: Combobox$1,
21101
- template: template$q,
21139
+ template: template$r,
21102
21140
  styles: styles$A,
21103
21141
  shadowOptions: {
21104
21142
  delegatesFocus: true
@@ -21246,7 +21284,7 @@
21246
21284
  }
21247
21285
  `));
21248
21286
 
21249
- const template$p = html `
21287
+ const template$q = html `
21250
21288
  <template>
21251
21289
  <dialog
21252
21290
  ${ref('dialogElement')}
@@ -21373,7 +21411,7 @@
21373
21411
  applyMixins(Dialog, ARIAGlobalStatesAndProperties);
21374
21412
  const nimbleDialog = Dialog.compose({
21375
21413
  baseName: 'dialog',
21376
- template: template$p,
21414
+ template: template$q,
21377
21415
  styles: styles$z,
21378
21416
  baseClass: Dialog
21379
21417
  });
@@ -21533,7 +21571,7 @@
21533
21571
  }
21534
21572
  `));
21535
21573
 
21536
- const template$o = html `
21574
+ const template$p = html `
21537
21575
  <dialog
21538
21576
  ${ref('dialog')}
21539
21577
  aria-label="${x => x.ariaLabel}"
@@ -21647,7 +21685,7 @@
21647
21685
  applyMixins(Drawer, ARIAGlobalStatesAndProperties);
21648
21686
  const nimbleDrawer = Drawer.compose({
21649
21687
  baseName: 'drawer',
21650
- template: template$o,
21688
+ template: template$p,
21651
21689
  styles: styles$y
21652
21690
  });
21653
21691
  DesignSystem.getOrCreate().withPrefix('nimble').register(nimbleDrawer());
@@ -24031,6 +24069,9 @@
24031
24069
 
24032
24070
  .content {
24033
24071
  padding: 8px 4px;
24072
+ white-space: nowrap;
24073
+ overflow: hidden;
24074
+ text-overflow: ellipsis;
24034
24075
  }
24035
24076
 
24036
24077
  :host([aria-selected='true']) {
@@ -24079,30 +24120,61 @@
24079
24120
  }
24080
24121
  `;
24081
24122
 
24123
+ // Based on template in FAST repo: https://github.com/microsoft/fast/blob/2ea80697bc3a5193e6123fb08ac3be2a76571aeb/packages/web-components/fast-foundation/src/listbox-option/listbox-option.template.ts
24124
+ /**
24125
+ * The template for the {@link @microsoft/fast-foundation#(ListboxOption:class)} component.
24126
+ * @public
24127
+ */
24128
+ // prettier-ignore
24129
+ const template$o = (context, definition) => html `
24130
+ <template
24131
+ aria-checked="${x => x.ariaChecked}"
24132
+ aria-disabled="${x => x.ariaDisabled}"
24133
+ aria-posinset="${x => x.ariaPosInSet}"
24134
+ aria-selected="${x => x.ariaSelected}"
24135
+ aria-setsize="${x => x.ariaSetSize}"
24136
+ class="${x => [x.checked && 'checked', x.selected && 'selected', x.disabled && 'disabled']
24137
+ .filter(Boolean)
24138
+ .join(' ')}"
24139
+ role="option"
24140
+ >
24141
+ ${startSlotTemplate(context, definition)}
24142
+ <span
24143
+ class="content"
24144
+ part="content"
24145
+ ${overflow('hasOverflow')}
24146
+ title=${x => (x.hasOverflow && x.elementTextContent ? x.elementTextContent : null)}
24147
+ >
24148
+ <slot ${ref('contentSlot')} ${slotted('content')}></slot>
24149
+ </span>
24150
+ ${endSlotTemplate(context, definition)}
24151
+ </template>
24152
+ `;
24153
+
24082
24154
  /**
24083
24155
  * A nimble-styled HTML listbox option
24084
24156
  */
24085
24157
  class ListOption extends ListboxOption {
24086
- // Workaround for https://github.com/microsoft/fast/issues/5219
24087
- get value() {
24088
- return super.value;
24089
- }
24090
- set value(value) {
24091
- // Coerce value to string
24092
- super.value = `${value}`;
24093
- if (this.$fastController.isConnected) {
24094
- this.setAttribute('value', this.value);
24095
- }
24158
+ constructor() {
24159
+ super(...arguments);
24160
+ /** @internal */
24161
+ this.hasOverflow = false;
24096
24162
  }
24097
- connectedCallback() {
24098
- super.connectedCallback();
24099
- this.setAttribute('value', this.value);
24163
+ /** @internal */
24164
+ get elementTextContent() {
24165
+ return this.contentSlot
24166
+ .assignedNodes()
24167
+ .map(node => node.textContent?.trim())
24168
+ .join(' ');
24100
24169
  }
24101
24170
  }
24171
+ __decorate$1([
24172
+ observable
24173
+ ], ListOption.prototype, "hasOverflow", void 0);
24102
24174
  const nimbleListOption = ListOption.compose({
24103
24175
  baseName: 'list-option',
24104
24176
  baseClass: ListboxOption,
24105
- template: listboxOptionTemplate,
24177
+ template: template$o,
24106
24178
  styles: styles$x
24107
24179
  });
24108
24180
  DesignSystem.getOrCreate().withPrefix('nimble').register(nimbleListOption());
@@ -58807,12 +58879,18 @@ img.ProseMirror-separator {
58807
58879
  * DOM structure using a DOMSerializer, and returns the serialized result.
58808
58880
  * If the markdown parser returns null, it will clear the viewer component by creating an empty document fragment.
58809
58881
  */
58810
- static parseMarkdownToDOM(value) {
58811
- const parsedMarkdownContent = this.markdownParser.parse(value);
58812
- if (parsedMarkdownContent === null) {
58813
- return document.createDocumentFragment();
58882
+ static parseMarkdownToDOM(value, markdownParserMentionConfig) {
58883
+ try {
58884
+ this.mentionConfigs = markdownParserMentionConfig;
58885
+ const parsedMarkdownContent = this.markdownParser.parse(value);
58886
+ if (parsedMarkdownContent === null) {
58887
+ return document.createDocumentFragment();
58888
+ }
58889
+ return this.domSerializer.serializeFragment(parsedMarkdownContent.content);
58890
+ }
58891
+ finally {
58892
+ this.mentionConfigs = undefined;
58814
58893
  }
58815
- return this.domSerializer.serializeFragment(parsedMarkdownContent.content);
58816
58894
  }
58817
58895
  static initializeMarkdownParser() {
58818
58896
  /**
@@ -58831,7 +58909,6 @@ img.ProseMirror-separator {
58831
58909
  'autolink',
58832
58910
  'newline'
58833
58911
  ]);
58834
- supportedTokenizerRules.validateLink = href => /^https?:\/\//i.test(href);
58835
58912
  /**
58836
58913
  * In order to display encoded characters, non-ASCII characters, emojis, and other special characters in their original form,
58837
58914
  * we bypass the default normalization of link text in markdown-it. This is done because we support only "AutoLink" feature in CommonMark flavor.
@@ -58843,7 +58920,7 @@ img.ProseMirror-separator {
58843
58920
  supportedTokenizerRules.normalizeLinkText = url => url;
58844
58921
  return new MarkdownParser(this.updatedSchema, supportedTokenizerRules, defaultMarkdownParser.tokens);
58845
58922
  }
58846
- static getSchemaWithLinkConfiguration() {
58923
+ static getCustomSchemaConfiguration() {
58847
58924
  return new Schema({
58848
58925
  nodes: schema.spec.nodes,
58849
58926
  marks: {
@@ -58859,10 +58936,30 @@ img.ProseMirror-separator {
58859
58936
  // See: https://github.com/ni/nimble/issues/1527
58860
58937
  excludes: '_',
58861
58938
  toDOM(node) {
58939
+ const href = node.attrs.href;
58940
+ const currentMention = RichTextMarkdownParser.mentionConfigs?.find(mention => mention.isValidMentionHref(href));
58941
+ const displayName = currentMention?.getDisplayName(href);
58942
+ if (currentMention && displayName) {
58943
+ return [
58944
+ currentMention.viewElement,
58945
+ {
58946
+ 'mention-href': href,
58947
+ 'mention-label': displayName,
58948
+ 'disable-editing': true
58949
+ }
58950
+ ];
58951
+ }
58862
58952
  return [
58863
58953
  anchorTag,
58864
58954
  {
58865
- href: node.attrs.href,
58955
+ /**
58956
+ * Both mention and absolute link markdown share the autolink format in CommonMark flavor.
58957
+ * Absolute links with HTTP/HTTPS will be rendered as links. Absolute links that match the
58958
+ * mention pattern will be rendered as mention view element. Absolute links without HTTP/HTTPS
58959
+ * scheme and no matching mention pattern will be rendered as plain text (anchor with no href).
58960
+ * With this, the user can click the links only when the scheme is HTTP/HTTPS
58961
+ */
58962
+ href: /^https?:\/\//i.test(href) ? href : null,
58866
58963
  rel: node.attrs.rel
58867
58964
  }
58868
58965
  ];
@@ -58875,7 +58972,7 @@ img.ProseMirror-separator {
58875
58972
  }
58876
58973
  }
58877
58974
  _a$1 = RichTextMarkdownParser;
58878
- RichTextMarkdownParser.updatedSchema = _a$1.getSchemaWithLinkConfiguration();
58975
+ RichTextMarkdownParser.updatedSchema = _a$1.getCustomSchemaConfiguration();
58879
58976
  RichTextMarkdownParser.markdownParser = _a$1.initializeMarkdownParser();
58880
58977
  RichTextMarkdownParser.domSerializer = DOMSerializer.fromSchema(_a$1.updatedSchema);
58881
58978
 
@@ -59445,7 +59542,9 @@ img.ProseMirror-separator {
59445
59542
  DesignSystem.tagFor(RichTextEditor);
59446
59543
 
59447
59544
  const template$k = html `
59448
- <div ${ref('viewer')} class="viewer"></div>
59545
+ <template ${children$1({ property: 'childItems', filter: elements() })}>
59546
+ <div ${ref('viewer')} class="viewer"></div>
59547
+ </template>
59449
59548
  `;
59450
59549
 
59451
59550
  const styles$o = css `
@@ -59486,8 +59585,150 @@ img.ProseMirror-separator {
59486
59585
  li > p:empty {
59487
59586
  display: none;
59488
59587
  }
59588
+
59589
+ ${
59590
+ /**
59591
+ * When an absolute link is not HTTPS/HTTP, the anchor tag renders without an `href`, appearing as plain text.
59592
+ * However, the `nimble-anchor` displays differently in color when the `href` attribute is absent.
59593
+ * To ensure a consistent appearance, the font color is forced to the default link color regardless of the `href`
59594
+ * attribute's presence.
59595
+ *
59596
+ * See models/markdown-parser.ts where link elements are emitted for more info.
59597
+ */ ''}
59598
+ nimble-anchor::part(control) {
59599
+ color: ${linkFontColor};
59600
+ }
59489
59601
  `;
59490
59602
 
59603
+ /**
59604
+ * Internal mention state
59605
+ */
59606
+ class MentionInternals {
59607
+ constructor(options) {
59608
+ /**
59609
+ * Whether this mention has a valid configuration.
59610
+ */
59611
+ this.validConfiguration = true;
59612
+ this.icon = options.icon;
59613
+ this.character = options.character;
59614
+ this.viewElement = options.viewElement;
59615
+ }
59616
+ }
59617
+ __decorate$1([
59618
+ observable
59619
+ ], MentionInternals.prototype, "mentionConfig", void 0);
59620
+ __decorate$1([
59621
+ observable
59622
+ ], MentionInternals.prototype, "validConfiguration", void 0);
59623
+
59624
+ /**
59625
+ * The base class for Mention configuration
59626
+ */
59627
+ class RichTextMention extends FoundationElement {
59628
+ constructor() {
59629
+ super(...arguments);
59630
+ /**
59631
+ * @internal
59632
+ */
59633
+ this.mentionInternals = new MentionInternals(this.getMentionInternalsOptions());
59634
+ /** @internal */
59635
+ this.validator = this.createValidator();
59636
+ /** @internal */
59637
+ this.mappingNotifiers = [];
59638
+ /** @internal */
59639
+ this.mappings = [];
59640
+ }
59641
+ checkValidity() {
59642
+ return this.mentionInternals.validConfiguration;
59643
+ }
59644
+ get validity() {
59645
+ return this.validator.getValidity();
59646
+ }
59647
+ /**
59648
+ * @internal
59649
+ */
59650
+ handleChange(source, args) {
59651
+ if (source instanceof Mapping$1 && typeof args === 'string') {
59652
+ this.updateMentionConfig();
59653
+ }
59654
+ }
59655
+ getMappingConfigs() {
59656
+ const mappingConfigs = new Map();
59657
+ this.mappings.forEach(mapping => {
59658
+ const href = mapping.key ?? undefined;
59659
+ if (href === undefined || typeof href !== 'string') {
59660
+ throw Error('mentionHref was invalid for type. Validation should have prevented this.');
59661
+ }
59662
+ const mappingConfig = this.createMappingConfig(mapping);
59663
+ mappingConfigs.set(href, mappingConfig);
59664
+ });
59665
+ return mappingConfigs;
59666
+ }
59667
+ /**
59668
+ * Called when any Mapping related state has changed.
59669
+ */
59670
+ updateMentionConfig() {
59671
+ this.validator.validate(this.mappings, this.pattern);
59672
+ this.mentionInternals.mentionConfig = this.validator.isValid()
59673
+ ? this.createMentionConfig(this.getMappingConfigs())
59674
+ : undefined;
59675
+ }
59676
+ mappingsChanged() {
59677
+ this.updateMentionConfig();
59678
+ this.observeMappings();
59679
+ }
59680
+ patternChanged() {
59681
+ this.mentionInternals.pattern = this.pattern;
59682
+ this.updateMentionConfig();
59683
+ }
59684
+ removeMappingObservers() {
59685
+ this.mappingNotifiers.forEach(notifier => {
59686
+ notifier.unsubscribe(this);
59687
+ });
59688
+ this.mappingNotifiers = [];
59689
+ }
59690
+ observeMappings() {
59691
+ this.removeMappingObservers();
59692
+ for (const mapping of this.mappings) {
59693
+ const notifier = Observable.getNotifier(mapping);
59694
+ notifier.subscribe(this);
59695
+ this.mappingNotifiers.push(notifier);
59696
+ }
59697
+ }
59698
+ }
59699
+ __decorate$1([
59700
+ attr
59701
+ ], RichTextMention.prototype, "pattern", void 0);
59702
+ __decorate$1([
59703
+ observable
59704
+ ], RichTextMention.prototype, "mappings", void 0);
59705
+
59706
+ /**
59707
+ * A configuration object for a Markdown parser, to be used by the viewer and editor components.
59708
+ * This object maintains the necessary internal values for handling mentions within the Markdown parser.
59709
+ */
59710
+ class MarkdownParserMentionConfiguration {
59711
+ constructor(mentionInternals) {
59712
+ this.regexPattern = new RegExp(mentionInternals.pattern ?? '');
59713
+ this.mappingConfigs = mentionInternals.mentionConfig?.mappingConfigs;
59714
+ this.viewElement = mentionInternals.viewElement;
59715
+ }
59716
+ isValidMentionHref(mentionHref) {
59717
+ return this.regexPattern.test(mentionHref);
59718
+ }
59719
+ getDisplayName(mentionHref) {
59720
+ const mentionMapping = this.mappingConfigs?.get(mentionHref);
59721
+ const mentionId = this.extractMentionId(mentionHref);
59722
+ return mentionMapping?.displayName ?? mentionId;
59723
+ }
59724
+ extractMentionId(mentionHref) {
59725
+ const regexpArray = this.regexPattern.exec(mentionHref);
59726
+ // Matches and gets the first group specified in the regex pattern
59727
+ // that renders as an alternative to the display name if missing.
59728
+ return regexpArray?.[1] ?? undefined;
59729
+ }
59730
+ }
59731
+
59491
59732
  /**
59492
59733
  * A nimble styled rich text viewer
59493
59734
  */
@@ -59500,6 +59741,19 @@ img.ProseMirror-separator {
59500
59741
  * Markdown string to render its corresponding rich text content in the component.
59501
59742
  */
59502
59743
  this.markdown = '';
59744
+ /**
59745
+ * @internal
59746
+ */
59747
+ this.mentionElements = [];
59748
+ /**
59749
+ * @internal
59750
+ */
59751
+ this.mentionInternalsConfig = [];
59752
+ /**
59753
+ * @internal
59754
+ */
59755
+ this.childItems = [];
59756
+ this.mentionInternalsNotifiers = [];
59503
59757
  }
59504
59758
  /**
59505
59759
  * @internal
@@ -59512,13 +59766,56 @@ img.ProseMirror-separator {
59512
59766
  * @internal
59513
59767
  */
59514
59768
  markdownChanged() {
59515
- if (this.$fastController.isConnected) {
59516
- this.updateView();
59769
+ this.updateView();
59770
+ }
59771
+ /**
59772
+ * @internal
59773
+ */
59774
+ handleChange(source, args) {
59775
+ if (source instanceof MentionInternals && typeof args === 'string') {
59776
+ this.updateMentionInternalsConfig();
59777
+ }
59778
+ }
59779
+ childItemsChanged() {
59780
+ void this.updateMentionsFromChildItems();
59781
+ }
59782
+ async updateMentionsFromChildItems() {
59783
+ const definedElements = this.childItems.map(async (item) => (item.matches(':not(:defined)')
59784
+ ? customElements.whenDefined(item.localName)
59785
+ : Promise.resolve()));
59786
+ await Promise.all(definedElements);
59787
+ this.mentionElements = this.childItems.filter((x) => x instanceof RichTextMention);
59788
+ this.observeMentions();
59789
+ this.updateMentionInternalsConfig();
59790
+ }
59791
+ observeMentions() {
59792
+ this.removeMentionObservers();
59793
+ for (const mention of this.mentionElements) {
59794
+ const notifierInternals = Observable.getNotifier(mention.mentionInternals);
59795
+ notifierInternals.subscribe(this);
59796
+ this.mentionInternalsNotifiers.push(notifierInternals);
59517
59797
  }
59518
59798
  }
59799
+ removeMentionObservers() {
59800
+ this.mentionInternalsNotifiers.forEach(notifier => {
59801
+ notifier.unsubscribe(this);
59802
+ });
59803
+ this.mentionInternalsNotifiers = [];
59804
+ }
59805
+ updateMentionInternalsConfig() {
59806
+ // TODO: Add a rich text validator to check if the `mentionElements` contains duplicate configuration element
59807
+ // For example, having two `nimble-rich-text-mention-users` within the children of rich text viewer or editor is an invalid configuration
59808
+ this.mentionInternalsConfig = this.mentionElements
59809
+ .filter(mention => mention.mentionInternals.validConfiguration)
59810
+ .map(mention => new MarkdownParserMentionConfiguration(mention.mentionInternals));
59811
+ this.updateView();
59812
+ }
59519
59813
  updateView() {
59814
+ if (!this.$fastController.isConnected) {
59815
+ return;
59816
+ }
59520
59817
  if (this.markdown) {
59521
- const serializedContent = RichTextMarkdownParser.parseMarkdownToDOM(this.markdown);
59818
+ const serializedContent = RichTextMarkdownParser.parseMarkdownToDOM(this.markdown, this.mentionInternalsConfig);
59522
59819
  this.viewer.replaceChildren(serializedContent);
59523
59820
  }
59524
59821
  else {
@@ -59529,6 +59826,9 @@ img.ProseMirror-separator {
59529
59826
  __decorate$1([
59530
59827
  observable
59531
59828
  ], RichTextViewer.prototype, "markdown", void 0);
59829
+ __decorate$1([
59830
+ observable
59831
+ ], RichTextViewer.prototype, "childItems", void 0);
59532
59832
  const nimbleRichTextViewer = RichTextViewer.compose({
59533
59833
  baseName: 'rich-text-viewer',
59534
59834
  template: template$k,
@@ -59603,7 +59903,7 @@ img.ProseMirror-separator {
59603
59903
  >
59604
59904
  ${startSlotTemplate(context, definition)}
59605
59905
  <slot name="button-container">
59606
- <div class="selected-value" part="selected-value">
59906
+ <div class="selected-value" part="selected-value" ${overflow('hasOverflow')} title=${x => (x.hasOverflow && x.displayValue ? x.displayValue : null)}>
59607
59907
  <slot name="selected-value">${x => x.displayValue}</slot>
59608
59908
  </div>
59609
59909
  <div aria-hidden="true" class="indicator" part="indicator">
@@ -59654,6 +59954,8 @@ img.ProseMirror-separator {
59654
59954
  super(...arguments);
59655
59955
  this.appearance = DropdownAppearance.underline;
59656
59956
  this.errorVisible = false;
59957
+ /** @internal */
59958
+ this.hasOverflow = false;
59657
59959
  }
59658
59960
  // Workaround for https://github.com/microsoft/fast/issues/5123
59659
59961
  setPositioning() {
@@ -59704,6 +60006,9 @@ img.ProseMirror-separator {
59704
60006
  __decorate$1([
59705
60007
  observable
59706
60008
  ], Select.prototype, "region", void 0);
60009
+ __decorate$1([
60010
+ observable
60011
+ ], Select.prototype, "hasOverflow", void 0);
59707
60012
  const nimbleSelect = Select.compose({
59708
60013
  baseName: 'select',
59709
60014
  baseClass: Select$1,
@@ -67512,61 +67817,6 @@ img.ProseMirror-separator {
67512
67817
  }
67513
67818
  `;
67514
67819
 
67515
- /**
67516
- * The runtime behavior for template overflow detection.
67517
- * @public
67518
- */
67519
- class OverflowBehavior {
67520
- /**
67521
- * Creates an instance of OverflowBehavior.
67522
- * @param target - The element to check for overflow.
67523
- * @param propertyName - The name of the property to assign the overflow state to.
67524
- */
67525
- constructor(target, propertyName) {
67526
- this.target = target;
67527
- this.propertyName = propertyName;
67528
- }
67529
- /**
67530
- * Bind this behavior to the source.
67531
- * @param source - The source to bind to.
67532
- * @param context - The execution context that the binding is operating within.
67533
- */
67534
- bind(source) {
67535
- this.source = source;
67536
- this.setSourceValue(false);
67537
- this.mouseOverHandler = () => {
67538
- const hasOverflow = this.target.offsetWidth < this.target.scrollWidth;
67539
- this.setSourceValue(hasOverflow);
67540
- };
67541
- this.mouseOutHandler = () => {
67542
- this.setSourceValue(false);
67543
- };
67544
- this.target.addEventListener('mouseover', this.mouseOverHandler);
67545
- this.target.addEventListener('mouseout', this.mouseOutHandler);
67546
- }
67547
- /**
67548
- * Unbinds this behavior from the source.
67549
- * @param source - The source to unbind from.
67550
- */
67551
- unbind() {
67552
- this.source = undefined;
67553
- this.target.removeEventListener('mouseover', this.mouseOverHandler);
67554
- this.target.removeEventListener('mouseout', this.mouseOutHandler);
67555
- }
67556
- setSourceValue(value) {
67557
- // @ts-expect-error set property on source
67558
- this.source[this.propertyName] = value;
67559
- }
67560
- }
67561
- /**
67562
- * A directive that observes if an element has overflow and sets a flag.
67563
- * @param propertyName - The name of the property to assign the overflow flag.
67564
- * @public
67565
- */
67566
- function overflow(propertyName) {
67567
- return new AttachedBehaviorHTMLDirective('nimble-overflow', OverflowBehavior, propertyName);
67568
- }
67569
-
67570
67820
  // Avoiding a wrapping <template> and be careful about starting and ending whitspace
67571
67821
  // so the template can be composed into other column header templates
67572
67822
  // prettier-ignore