@ni/nimble-components 20.14.8 → 20.14.9

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 (29) hide show
  1. package/dist/all-components-bundle.js +242 -14
  2. package/dist/all-components-bundle.js.map +1 -1
  3. package/dist/all-components-bundle.min.js +1057 -1035
  4. package/dist/all-components-bundle.min.js.map +1 -1
  5. package/dist/esm/rich-text/models/markdown-parser-mention-configuration.d.ts +15 -0
  6. package/dist/esm/rich-text/models/markdown-parser-mention-configuration.js +26 -0
  7. package/dist/esm/rich-text/models/markdown-parser-mention-configuration.js.map +1 -0
  8. package/dist/esm/rich-text/models/markdown-parser.d.ts +9 -2
  9. package/dist/esm/rich-text/models/markdown-parser.js +34 -9
  10. package/dist/esm/rich-text/models/markdown-parser.js.map +1 -1
  11. package/dist/esm/rich-text/models/testing/markdown-parser-utils.d.ts +1 -0
  12. package/dist/esm/rich-text/models/testing/markdown-parser-utils.js +3 -0
  13. package/dist/esm/rich-text/models/testing/markdown-parser-utils.js.map +1 -1
  14. package/dist/esm/rich-text/viewer/index.d.ts +24 -0
  15. package/dist/esm/rich-text/viewer/index.js +66 -4
  16. package/dist/esm/rich-text/viewer/index.js.map +1 -1
  17. package/dist/esm/rich-text/viewer/styles.js +14 -1
  18. package/dist/esm/rich-text/viewer/styles.js.map +1 -1
  19. package/dist/esm/rich-text/viewer/template.js +4 -2
  20. package/dist/esm/rich-text/viewer/template.js.map +1 -1
  21. package/dist/esm/rich-text/viewer/testing/rich-text-viewer.pageobject.d.ts +1 -0
  22. package/dist/esm/rich-text/viewer/testing/rich-text-viewer.pageobject.js +6 -3
  23. package/dist/esm/rich-text/viewer/testing/rich-text-viewer.pageobject.js.map +1 -1
  24. package/dist/esm/rich-text-mention/base/models/mention-internals.d.ts +5 -0
  25. package/dist/esm/rich-text-mention/base/models/mention-internals.js +1 -0
  26. package/dist/esm/rich-text-mention/base/models/mention-internals.js.map +1 -1
  27. package/dist/esm/rich-text-mention/users/index.js +3 -1
  28. package/dist/esm/rich-text-mention/users/index.js.map +1 -1
  29. package/package.json +1 -1
@@ -16298,7 +16298,7 @@
16298
16298
 
16299
16299
  /**
16300
16300
  * Do not edit directly
16301
- * Generated on Tue, 21 Nov 2023 19:01:43 GMT
16301
+ * Generated on Thu, 23 Nov 2023 07:56:58 GMT
16302
16302
  */
16303
16303
 
16304
16304
  const Information100DarkUi = "#a46eff";
@@ -58807,12 +58807,18 @@ img.ProseMirror-separator {
58807
58807
  * DOM structure using a DOMSerializer, and returns the serialized result.
58808
58808
  * If the markdown parser returns null, it will clear the viewer component by creating an empty document fragment.
58809
58809
  */
58810
- static parseMarkdownToDOM(value) {
58811
- const parsedMarkdownContent = this.markdownParser.parse(value);
58812
- if (parsedMarkdownContent === null) {
58813
- return document.createDocumentFragment();
58810
+ static parseMarkdownToDOM(value, markdownParserMentionConfig) {
58811
+ try {
58812
+ this.mentionConfigs = markdownParserMentionConfig;
58813
+ const parsedMarkdownContent = this.markdownParser.parse(value);
58814
+ if (parsedMarkdownContent === null) {
58815
+ return document.createDocumentFragment();
58816
+ }
58817
+ return this.domSerializer.serializeFragment(parsedMarkdownContent.content);
58818
+ }
58819
+ finally {
58820
+ this.mentionConfigs = undefined;
58814
58821
  }
58815
- return this.domSerializer.serializeFragment(parsedMarkdownContent.content);
58816
58822
  }
58817
58823
  static initializeMarkdownParser() {
58818
58824
  /**
@@ -58831,7 +58837,6 @@ img.ProseMirror-separator {
58831
58837
  'autolink',
58832
58838
  'newline'
58833
58839
  ]);
58834
- supportedTokenizerRules.validateLink = href => /^https?:\/\//i.test(href);
58835
58840
  /**
58836
58841
  * In order to display encoded characters, non-ASCII characters, emojis, and other special characters in their original form,
58837
58842
  * 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 +58848,7 @@ img.ProseMirror-separator {
58843
58848
  supportedTokenizerRules.normalizeLinkText = url => url;
58844
58849
  return new MarkdownParser(this.updatedSchema, supportedTokenizerRules, defaultMarkdownParser.tokens);
58845
58850
  }
58846
- static getSchemaWithLinkConfiguration() {
58851
+ static getCustomSchemaConfiguration() {
58847
58852
  return new Schema({
58848
58853
  nodes: schema.spec.nodes,
58849
58854
  marks: {
@@ -58859,10 +58864,30 @@ img.ProseMirror-separator {
58859
58864
  // See: https://github.com/ni/nimble/issues/1527
58860
58865
  excludes: '_',
58861
58866
  toDOM(node) {
58867
+ const href = node.attrs.href;
58868
+ const currentMention = RichTextMarkdownParser.mentionConfigs?.find(mention => mention.isValidMentionHref(href));
58869
+ const displayName = currentMention?.getDisplayName(href);
58870
+ if (currentMention && displayName) {
58871
+ return [
58872
+ currentMention.viewElement,
58873
+ {
58874
+ 'mention-href': href,
58875
+ 'mention-label': displayName,
58876
+ 'disable-editing': true
58877
+ }
58878
+ ];
58879
+ }
58862
58880
  return [
58863
58881
  anchorTag,
58864
58882
  {
58865
- href: node.attrs.href,
58883
+ /**
58884
+ * Both mention and absolute link markdown share the autolink format in CommonMark flavor.
58885
+ * Absolute links with HTTP/HTTPS will be rendered as links. Absolute links that match the
58886
+ * mention pattern will be rendered as mention view element. Absolute links without HTTP/HTTPS
58887
+ * scheme and no matching mention pattern will be rendered as plain text (anchor with no href).
58888
+ * With this, the user can click the links only when the scheme is HTTP/HTTPS
58889
+ */
58890
+ href: /^https?:\/\//i.test(href) ? href : null,
58866
58891
  rel: node.attrs.rel
58867
58892
  }
58868
58893
  ];
@@ -58875,7 +58900,7 @@ img.ProseMirror-separator {
58875
58900
  }
58876
58901
  }
58877
58902
  _a$1 = RichTextMarkdownParser;
58878
- RichTextMarkdownParser.updatedSchema = _a$1.getSchemaWithLinkConfiguration();
58903
+ RichTextMarkdownParser.updatedSchema = _a$1.getCustomSchemaConfiguration();
58879
58904
  RichTextMarkdownParser.markdownParser = _a$1.initializeMarkdownParser();
58880
58905
  RichTextMarkdownParser.domSerializer = DOMSerializer.fromSchema(_a$1.updatedSchema);
58881
58906
 
@@ -59445,7 +59470,9 @@ img.ProseMirror-separator {
59445
59470
  DesignSystem.tagFor(RichTextEditor);
59446
59471
 
59447
59472
  const template$k = html `
59448
- <div ${ref('viewer')} class="viewer"></div>
59473
+ <template ${children$1({ property: 'childItems', filter: elements() })}>
59474
+ <div ${ref('viewer')} class="viewer"></div>
59475
+ </template>
59449
59476
  `;
59450
59477
 
59451
59478
  const styles$o = css `
@@ -59486,8 +59513,150 @@ img.ProseMirror-separator {
59486
59513
  li > p:empty {
59487
59514
  display: none;
59488
59515
  }
59516
+
59517
+ ${
59518
+ /**
59519
+ * When an absolute link is not HTTPS/HTTP, the anchor tag renders without an `href`, appearing as plain text.
59520
+ * However, the `nimble-anchor` displays differently in color when the `href` attribute is absent.
59521
+ * To ensure a consistent appearance, the font color is forced to the default link color regardless of the `href`
59522
+ * attribute's presence.
59523
+ *
59524
+ * See models/markdown-parser.ts where link elements are emitted for more info.
59525
+ */ ''}
59526
+ nimble-anchor::part(control) {
59527
+ color: ${linkFontColor};
59528
+ }
59489
59529
  `;
59490
59530
 
59531
+ /**
59532
+ * Internal mention state
59533
+ */
59534
+ class MentionInternals {
59535
+ constructor(options) {
59536
+ /**
59537
+ * Whether this mention has a valid configuration.
59538
+ */
59539
+ this.validConfiguration = true;
59540
+ this.icon = options.icon;
59541
+ this.character = options.character;
59542
+ this.viewElement = options.viewElement;
59543
+ }
59544
+ }
59545
+ __decorate$1([
59546
+ observable
59547
+ ], MentionInternals.prototype, "mentionConfig", void 0);
59548
+ __decorate$1([
59549
+ observable
59550
+ ], MentionInternals.prototype, "validConfiguration", void 0);
59551
+
59552
+ /**
59553
+ * The base class for Mention configuration
59554
+ */
59555
+ class RichTextMention extends FoundationElement {
59556
+ constructor() {
59557
+ super(...arguments);
59558
+ /**
59559
+ * @internal
59560
+ */
59561
+ this.mentionInternals = new MentionInternals(this.getMentionInternalsOptions());
59562
+ /** @internal */
59563
+ this.validator = this.createValidator();
59564
+ /** @internal */
59565
+ this.mappingNotifiers = [];
59566
+ /** @internal */
59567
+ this.mappings = [];
59568
+ }
59569
+ checkValidity() {
59570
+ return this.mentionInternals.validConfiguration;
59571
+ }
59572
+ get validity() {
59573
+ return this.validator.getValidity();
59574
+ }
59575
+ /**
59576
+ * @internal
59577
+ */
59578
+ handleChange(source, args) {
59579
+ if (source instanceof Mapping$1 && typeof args === 'string') {
59580
+ this.updateMentionConfig();
59581
+ }
59582
+ }
59583
+ getMappingConfigs() {
59584
+ const mappingConfigs = new Map();
59585
+ this.mappings.forEach(mapping => {
59586
+ const href = mapping.key ?? undefined;
59587
+ if (href === undefined || typeof href !== 'string') {
59588
+ throw Error('mentionHref was invalid for type. Validation should have prevented this.');
59589
+ }
59590
+ const mappingConfig = this.createMappingConfig(mapping);
59591
+ mappingConfigs.set(href, mappingConfig);
59592
+ });
59593
+ return mappingConfigs;
59594
+ }
59595
+ /**
59596
+ * Called when any Mapping related state has changed.
59597
+ */
59598
+ updateMentionConfig() {
59599
+ this.validator.validate(this.mappings, this.pattern);
59600
+ this.mentionInternals.mentionConfig = this.validator.isValid()
59601
+ ? this.createMentionConfig(this.getMappingConfigs())
59602
+ : undefined;
59603
+ }
59604
+ mappingsChanged() {
59605
+ this.updateMentionConfig();
59606
+ this.observeMappings();
59607
+ }
59608
+ patternChanged() {
59609
+ this.mentionInternals.pattern = this.pattern;
59610
+ this.updateMentionConfig();
59611
+ }
59612
+ removeMappingObservers() {
59613
+ this.mappingNotifiers.forEach(notifier => {
59614
+ notifier.unsubscribe(this);
59615
+ });
59616
+ this.mappingNotifiers = [];
59617
+ }
59618
+ observeMappings() {
59619
+ this.removeMappingObservers();
59620
+ for (const mapping of this.mappings) {
59621
+ const notifier = Observable.getNotifier(mapping);
59622
+ notifier.subscribe(this);
59623
+ this.mappingNotifiers.push(notifier);
59624
+ }
59625
+ }
59626
+ }
59627
+ __decorate$1([
59628
+ attr
59629
+ ], RichTextMention.prototype, "pattern", void 0);
59630
+ __decorate$1([
59631
+ observable
59632
+ ], RichTextMention.prototype, "mappings", void 0);
59633
+
59634
+ /**
59635
+ * A configuration object for a Markdown parser, to be used by the viewer and editor components.
59636
+ * This object maintains the necessary internal values for handling mentions within the Markdown parser.
59637
+ */
59638
+ class MarkdownParserMentionConfiguration {
59639
+ constructor(mentionInternals) {
59640
+ this.regexPattern = new RegExp(mentionInternals.pattern ?? '');
59641
+ this.mappingConfigs = mentionInternals.mentionConfig?.mappingConfigs;
59642
+ this.viewElement = mentionInternals.viewElement;
59643
+ }
59644
+ isValidMentionHref(mentionHref) {
59645
+ return this.regexPattern.test(mentionHref);
59646
+ }
59647
+ getDisplayName(mentionHref) {
59648
+ const mentionMapping = this.mappingConfigs?.get(mentionHref);
59649
+ const mentionId = this.extractMentionId(mentionHref);
59650
+ return mentionMapping?.displayName ?? mentionId;
59651
+ }
59652
+ extractMentionId(mentionHref) {
59653
+ const regexpArray = this.regexPattern.exec(mentionHref);
59654
+ // Matches and gets the first group specified in the regex pattern
59655
+ // that renders as an alternative to the display name if missing.
59656
+ return regexpArray?.[1] ?? undefined;
59657
+ }
59658
+ }
59659
+
59491
59660
  /**
59492
59661
  * A nimble styled rich text viewer
59493
59662
  */
@@ -59500,6 +59669,19 @@ img.ProseMirror-separator {
59500
59669
  * Markdown string to render its corresponding rich text content in the component.
59501
59670
  */
59502
59671
  this.markdown = '';
59672
+ /**
59673
+ * @internal
59674
+ */
59675
+ this.mentionElements = [];
59676
+ /**
59677
+ * @internal
59678
+ */
59679
+ this.mentionInternalsConfig = [];
59680
+ /**
59681
+ * @internal
59682
+ */
59683
+ this.childItems = [];
59684
+ this.mentionInternalsNotifiers = [];
59503
59685
  }
59504
59686
  /**
59505
59687
  * @internal
@@ -59512,13 +59694,56 @@ img.ProseMirror-separator {
59512
59694
  * @internal
59513
59695
  */
59514
59696
  markdownChanged() {
59515
- if (this.$fastController.isConnected) {
59516
- this.updateView();
59697
+ this.updateView();
59698
+ }
59699
+ /**
59700
+ * @internal
59701
+ */
59702
+ handleChange(source, args) {
59703
+ if (source instanceof MentionInternals && typeof args === 'string') {
59704
+ this.updateMentionInternalsConfig();
59517
59705
  }
59518
59706
  }
59707
+ childItemsChanged() {
59708
+ void this.updateMentionsFromChildItems();
59709
+ }
59710
+ async updateMentionsFromChildItems() {
59711
+ const definedElements = this.childItems.map(async (item) => (item.matches(':not(:defined)')
59712
+ ? customElements.whenDefined(item.localName)
59713
+ : Promise.resolve()));
59714
+ await Promise.all(definedElements);
59715
+ this.mentionElements = this.childItems.filter((x) => x instanceof RichTextMention);
59716
+ this.observeMentions();
59717
+ this.updateMentionInternalsConfig();
59718
+ }
59719
+ observeMentions() {
59720
+ this.removeMentionObservers();
59721
+ for (const mention of this.mentionElements) {
59722
+ const notifierInternals = Observable.getNotifier(mention.mentionInternals);
59723
+ notifierInternals.subscribe(this);
59724
+ this.mentionInternalsNotifiers.push(notifierInternals);
59725
+ }
59726
+ }
59727
+ removeMentionObservers() {
59728
+ this.mentionInternalsNotifiers.forEach(notifier => {
59729
+ notifier.unsubscribe(this);
59730
+ });
59731
+ this.mentionInternalsNotifiers = [];
59732
+ }
59733
+ updateMentionInternalsConfig() {
59734
+ // TODO: Add a rich text validator to check if the `mentionElements` contains duplicate configuration element
59735
+ // For example, having two `nimble-rich-text-mention-users` within the children of rich text viewer or editor is an invalid configuration
59736
+ this.mentionInternalsConfig = this.mentionElements
59737
+ .filter(mention => mention.mentionInternals.validConfiguration)
59738
+ .map(mention => new MarkdownParserMentionConfiguration(mention.mentionInternals));
59739
+ this.updateView();
59740
+ }
59519
59741
  updateView() {
59742
+ if (!this.$fastController.isConnected) {
59743
+ return;
59744
+ }
59520
59745
  if (this.markdown) {
59521
- const serializedContent = RichTextMarkdownParser.parseMarkdownToDOM(this.markdown);
59746
+ const serializedContent = RichTextMarkdownParser.parseMarkdownToDOM(this.markdown, this.mentionInternalsConfig);
59522
59747
  this.viewer.replaceChildren(serializedContent);
59523
59748
  }
59524
59749
  else {
@@ -59529,6 +59754,9 @@ img.ProseMirror-separator {
59529
59754
  __decorate$1([
59530
59755
  observable
59531
59756
  ], RichTextViewer.prototype, "markdown", void 0);
59757
+ __decorate$1([
59758
+ observable
59759
+ ], RichTextViewer.prototype, "childItems", void 0);
59532
59760
  const nimbleRichTextViewer = RichTextViewer.compose({
59533
59761
  baseName: 'rich-text-viewer',
59534
59762
  template: template$k,