@ni/nimble-components 20.1.18 → 20.1.20

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.
@@ -16288,7 +16288,7 @@
16288
16288
 
16289
16289
  /**
16290
16290
  * Do not edit directly
16291
- * Generated on Wed, 30 Aug 2023 18:04:09 GMT
16291
+ * Generated on Thu, 31 Aug 2023 18:57:21 GMT
16292
16292
  */
16293
16293
 
16294
16294
  const Information100DarkUi = "#a46eff";
@@ -37836,9 +37836,6 @@
37836
37836
  buildProps(tr, shouldDispatch = true) {
37837
37837
  const { rawCommands, editor, state } = this;
37838
37838
  const { view } = editor;
37839
- if (state.storedMarks) {
37840
- tr.setStoredMarks(state.storedMarks);
37841
- }
37842
37839
  const props = {
37843
37840
  tr,
37844
37841
  editor,
@@ -37848,7 +37845,7 @@
37848
37845
  transaction: tr,
37849
37846
  }),
37850
37847
  dispatch: shouldDispatch ? () => undefined : undefined,
37851
- chain: () => this.createChain(tr),
37848
+ chain: () => this.createChain(tr, shouldDispatch),
37852
37849
  can: () => this.createCan(tr),
37853
37850
  get commands() {
37854
37851
  return Object.fromEntries(Object.entries(rawCommands).map(([name, command]) => {
@@ -38024,7 +38021,10 @@
38024
38021
  return;
38025
38022
  }
38026
38023
  if (key === 'class') {
38027
- mergedAttributes[key] = [mergedAttributes[key], value].join(' ');
38024
+ const valueClasses = value ? value.split(' ') : [];
38025
+ const existingClasses = mergedAttributes[key] ? mergedAttributes[key].split(' ') : [];
38026
+ const insertClasses = valueClasses.filter(valueClass => !existingClasses.includes(valueClass));
38027
+ mergedAttributes[key] = [...existingClasses, ...insertClasses].join(' ');
38028
38028
  }
38029
38029
  else if (key === 'style') {
38030
38030
  mergedAttributes[key] = [mergedAttributes[key], value].join('; ');
@@ -39027,6 +39027,16 @@
39027
39027
  return createParagraphNear$1(state, dispatch);
39028
39028
  };
39029
39029
 
39030
+ const cut = (originRange, targetPos) => ({ editor, tr }) => {
39031
+ const { state } = editor;
39032
+ const contentSlice = state.doc.slice(originRange.from, originRange.to);
39033
+ tr.deleteRange(originRange.from, originRange.to);
39034
+ const newPos = tr.mapping.map(targetPos);
39035
+ tr.insert(newPos, contentSlice.content);
39036
+ tr.setSelection(new TextSelection(tr.doc.resolve(newPos - 1)));
39037
+ return true;
39038
+ };
39039
+
39030
39040
  const deleteCurrentNode = () => ({ tr, dispatch }) => {
39031
39041
  const { selection } = tr;
39032
39042
  const currentNode = selection.$anchor.node();
@@ -39356,7 +39366,7 @@
39356
39366
  if (content.toString() === '<>') {
39357
39367
  return true;
39358
39368
  }
39359
- let { from, to } = typeof position === 'number' ? { from: position, to: position } : position;
39369
+ let { from, to } = typeof position === 'number' ? { from: position, to: position } : { from: position.from, to: position.to };
39360
39370
  let isOnlyTextContent = true;
39361
39371
  let isOnlyBlockContent = true;
39362
39372
  const nodes = isFragment(content) ? content : [content];
@@ -39418,6 +39428,40 @@
39418
39428
  return joinForward$1(state, dispatch);
39419
39429
  };
39420
39430
 
39431
+ const joinItemBackward = () => ({ tr, state, dispatch, }) => {
39432
+ try {
39433
+ const point = joinPoint(state.doc, state.selection.$from.pos, -1);
39434
+ if (point === null || point === undefined) {
39435
+ return false;
39436
+ }
39437
+ tr.join(point, 2);
39438
+ if (dispatch) {
39439
+ dispatch(tr);
39440
+ }
39441
+ return true;
39442
+ }
39443
+ catch {
39444
+ return false;
39445
+ }
39446
+ };
39447
+
39448
+ const joinItemForward = () => ({ state, dispatch, tr, }) => {
39449
+ try {
39450
+ const point = joinPoint(state.doc, state.selection.$from.pos, +1);
39451
+ if (point === null || point === undefined) {
39452
+ return false;
39453
+ }
39454
+ tr.join(point, 2);
39455
+ if (dispatch) {
39456
+ dispatch(tr);
39457
+ }
39458
+ return true;
39459
+ }
39460
+ catch (e) {
39461
+ return false;
39462
+ }
39463
+ };
39464
+
39421
39465
  function isMacOS() {
39422
39466
  return typeof navigator !== 'undefined'
39423
39467
  ? /Mac/.test(navigator.platform)
@@ -39664,6 +39708,28 @@
39664
39708
  return true;
39665
39709
  };
39666
39710
 
39711
+ function getMarkAttributes(state, typeOrName) {
39712
+ const type = getMarkType(typeOrName, state.schema);
39713
+ const { from, to, empty } = state.selection;
39714
+ const marks = [];
39715
+ if (empty) {
39716
+ if (state.storedMarks) {
39717
+ marks.push(...state.storedMarks);
39718
+ }
39719
+ marks.push(...state.selection.$head.marks());
39720
+ }
39721
+ else {
39722
+ state.doc.nodesBetween(from, to, node => {
39723
+ marks.push(...node.marks);
39724
+ });
39725
+ }
39726
+ const mark = marks.find(markItem => markItem.type.name === type.name);
39727
+ if (!mark) {
39728
+ return {};
39729
+ }
39730
+ return { ...mark.attrs };
39731
+ }
39732
+
39667
39733
  function defaultBlockAt(match) {
39668
39734
  for (let i = 0; i < match.edgeCount; i += 1) {
39669
39735
  const { type } = match.edge(i);
@@ -39708,28 +39774,6 @@
39708
39774
  return getTextBetween(node, range, options);
39709
39775
  }
39710
39776
 
39711
- function getMarkAttributes(state, typeOrName) {
39712
- const type = getMarkType(typeOrName, state.schema);
39713
- const { from, to, empty } = state.selection;
39714
- const marks = [];
39715
- if (empty) {
39716
- if (state.storedMarks) {
39717
- marks.push(...state.storedMarks);
39718
- }
39719
- marks.push(...state.selection.$head.marks());
39720
- }
39721
- else {
39722
- state.doc.nodesBetween(from, to, node => {
39723
- marks.push(...node.marks);
39724
- });
39725
- }
39726
- const mark = marks.find(markItem => markItem.type.name === type.name);
39727
- if (!mark) {
39728
- return {};
39729
- }
39730
- return { ...mark.attrs };
39731
- }
39732
-
39733
39777
  function getNodeAttributes(state, typeOrName) {
39734
39778
  const type = getNodeType(typeOrName, state.schema);
39735
39779
  const { from, to } = state.selection;
@@ -40448,6 +40492,7 @@
40448
40492
  clearNodes: clearNodes,
40449
40493
  command: command,
40450
40494
  createParagraphNear: createParagraphNear,
40495
+ cut: cut,
40451
40496
  deleteCurrentNode: deleteCurrentNode,
40452
40497
  deleteNode: deleteNode,
40453
40498
  deleteRange: deleteRange,
@@ -40464,6 +40509,8 @@
40464
40509
  joinDown: joinDown,
40465
40510
  joinBackward: joinBackward,
40466
40511
  joinForward: joinForward,
40512
+ joinItemBackward: joinItemBackward,
40513
+ joinItemForward: joinItemForward,
40467
40514
  keyboardShortcut: keyboardShortcut,
40468
40515
  lift: lift,
40469
40516
  liftEmptyBlock: liftEmptyBlock,
@@ -40563,7 +40610,12 @@
40563
40610
  const { selection, doc } = tr;
40564
40611
  const { empty, $anchor } = selection;
40565
40612
  const { pos, parent } = $anchor;
40566
- const isAtStart = Selection$2.atStart(doc).from === pos;
40613
+ const $parentPos = $anchor.parent.isTextblock ? tr.doc.resolve(pos - 1) : $anchor;
40614
+ const parentIsIsolating = $parentPos.parent.type.spec.isolating;
40615
+ const parentPos = $anchor.pos - $anchor.parentOffset;
40616
+ const isAtStart = (parentIsIsolating && $parentPos.parent.childCount === 1)
40617
+ ? parentPos === $anchor.pos
40618
+ : Selection$2.atStart(doc).from === pos;
40567
40619
  if (!empty || !isAtStart || !parent.type.isTextblock || parent.textContent.length) {
40568
40620
  return false;
40569
40621
  }
@@ -40976,6 +41028,7 @@ img.ProseMirror-separator {
40976
41028
  });
40977
41029
  this.view.updateState(newState);
40978
41030
  this.createNodeViews();
41031
+ this.prependClass();
40979
41032
  // Let’s store the editor instance in the DOM element.
40980
41033
  // So we’ll have access to it for tests.
40981
41034
  const dom = this.view.dom;
@@ -40989,6 +41042,12 @@ img.ProseMirror-separator {
40989
41042
  nodeViews: this.extensionManager.nodeViews,
40990
41043
  });
40991
41044
  }
41045
+ /**
41046
+ * Prepend class name to element.
41047
+ */
41048
+ prependClass() {
41049
+ this.view.dom.className = `tiptap ${this.view.dom.className}`;
41050
+ }
40992
41051
  captureTransaction(fn) {
40993
41052
  this.isCapturingTransaction = true;
40994
41053
  fn();
@@ -41139,7 +41198,6 @@ img.ProseMirror-separator {
41139
41198
  const { tr } = state;
41140
41199
  const captureGroup = match[match.length - 1];
41141
41200
  const fullMatch = match[0];
41142
- let markEnd = range.to;
41143
41201
  if (captureGroup) {
41144
41202
  const startSpaces = fullMatch.search(/\S/);
41145
41203
  const textStart = range.from + fullMatch.indexOf(captureGroup);
@@ -41160,7 +41218,7 @@ img.ProseMirror-separator {
41160
41218
  if (textStart > range.from) {
41161
41219
  tr.delete(range.from + startSpaces, textStart);
41162
41220
  }
41163
- markEnd = range.from + startSpaces + captureGroup.length;
41221
+ const markEnd = range.from + startSpaces + captureGroup.length;
41164
41222
  tr.addMark(range.from + startSpaces, markEnd, config.type.create(attributes || {}));
41165
41223
  tr.removeStoredMark(config.type);
41166
41224
  }
@@ -54653,6 +54711,8 @@ img.ProseMirror-separator {
54653
54711
  addOptions() {
54654
54712
  return {
54655
54713
  HTMLAttributes: {},
54714
+ bulletListTypeName: 'bulletList',
54715
+ orderedListTypeName: 'orderedList',
54656
54716
  };
54657
54717
  },
54658
54718
  content: 'paragraph block*',
@@ -55420,8 +55480,11 @@ img.ProseMirror-separator {
55420
55480
  addKeyboardShortcuts() {
55421
55481
  return {
55422
55482
  'Mod-z': () => this.editor.commands.undo(),
55483
+ 'Mod-Z': () => this.editor.commands.undo(),
55423
55484
  'Mod-y': () => this.editor.commands.redo(),
55485
+ 'Mod-Y': () => this.editor.commands.redo(),
55424
55486
  'Shift-Mod-z': () => this.editor.commands.redo(),
55487
+ 'Shift-Mod-Z': () => this.editor.commands.redo(),
55425
55488
  // Russian keyboard layouts
55426
55489
  'Mod-я': () => this.editor.commands.undo(),
55427
55490
  'Shift-Mod-я': () => this.editor.commands.redo(),
@@ -55507,6 +55570,8 @@ img.ProseMirror-separator {
55507
55570
  addOptions() {
55508
55571
  return {
55509
55572
  HTMLAttributes: {},
55573
+ bulletListTypeName: 'bulletList',
55574
+ orderedListTypeName: 'orderedList',
55510
55575
  };
55511
55576
  },
55512
55577
  content: 'paragraph block*',
@@ -55535,6 +55600,8 @@ img.ProseMirror-separator {
55535
55600
  addOptions() {
55536
55601
  return {
55537
55602
  HTMLAttributes: {},
55603
+ bulletListTypeName: 'bulletList',
55604
+ orderedListTypeName: 'orderedList',
55538
55605
  };
55539
55606
  },
55540
55607
  content: 'paragraph block*',
@@ -55707,6 +55774,65 @@ img.ProseMirror-separator {
55707
55774
  },
55708
55775
  });
55709
55776
 
55777
+ const Placeholder = Extension.create({
55778
+ name: 'placeholder',
55779
+ addOptions() {
55780
+ return {
55781
+ emptyEditorClass: 'is-editor-empty',
55782
+ emptyNodeClass: 'is-empty',
55783
+ placeholder: 'Write something …',
55784
+ showOnlyWhenEditable: true,
55785
+ showOnlyCurrent: true,
55786
+ includeChildren: false,
55787
+ };
55788
+ },
55789
+ addProseMirrorPlugins() {
55790
+ return [
55791
+ new Plugin({
55792
+ key: new PluginKey('placeholder'),
55793
+ props: {
55794
+ decorations: ({ doc, selection }) => {
55795
+ const active = this.editor.isEditable || !this.options.showOnlyWhenEditable;
55796
+ const { anchor } = selection;
55797
+ const decorations = [];
55798
+ if (!active) {
55799
+ return null;
55800
+ }
55801
+ // only calculate isEmpty once due to its performance impacts (see issue #3360)
55802
+ const emptyDocInstance = doc.type.createAndFill();
55803
+ const isEditorEmpty = (emptyDocInstance === null || emptyDocInstance === void 0 ? void 0 : emptyDocInstance.sameMarkup(doc))
55804
+ && emptyDocInstance.content.findDiffStart(doc.content) === null;
55805
+ doc.descendants((node, pos) => {
55806
+ const hasAnchor = anchor >= pos && anchor <= pos + node.nodeSize;
55807
+ const isEmpty = !node.isLeaf && !node.childCount;
55808
+ if ((hasAnchor || !this.options.showOnlyCurrent) && isEmpty) {
55809
+ const classes = [this.options.emptyNodeClass];
55810
+ if (isEditorEmpty) {
55811
+ classes.push(this.options.emptyEditorClass);
55812
+ }
55813
+ const decoration = Decoration.node(pos, pos + node.nodeSize, {
55814
+ class: classes.join(' '),
55815
+ 'data-placeholder': typeof this.options.placeholder === 'function'
55816
+ ? this.options.placeholder({
55817
+ editor: this.editor,
55818
+ node,
55819
+ pos,
55820
+ hasAnchor,
55821
+ })
55822
+ : this.options.placeholder,
55823
+ });
55824
+ decorations.push(decoration);
55825
+ }
55826
+ return this.options.includeChildren;
55827
+ });
55828
+ return DecorationSet.create(doc, decorations);
55829
+ },
55830
+ },
55831
+ }),
55832
+ ];
55833
+ },
55834
+ });
55835
+
55710
55836
  const Text = Node$1.create({
55711
55837
  name: 'text',
55712
55838
  group: 'inline',
@@ -55761,12 +55887,18 @@ img.ProseMirror-separator {
55761
55887
  <div class="container">
55762
55888
  <section ${ref('editorContainer')} class="editor-container">
55763
55889
  </section>
55764
- <section class="footer-section" part="footer-section">
55890
+ <${iconExclamationMarkTag}
55891
+ severity="error"
55892
+ class="error-icon ${x => (x.scrollbarWidth >= 0 ? 'scrollbar-width-calculated' : '')}"
55893
+ style="--ni-private-rich-text-editor-scrollbar-width: ${x => x.scrollbarWidth}px;"
55894
+ ></${iconExclamationMarkTag}>
55895
+ <section class="footer-section">
55765
55896
  <${toolbarTag}>
55766
55897
  <${toggleButtonTag}
55767
55898
  ${ref('boldButton')}
55768
55899
  appearance="ghost"
55769
55900
  content-hidden
55901
+ ?disabled="${x => x.disabled}"
55770
55902
  slot="start"
55771
55903
  title="Bold"
55772
55904
  @click=${x => x.boldButtonClick()}
@@ -55780,6 +55912,7 @@ img.ProseMirror-separator {
55780
55912
  ${ref('italicsButton')}
55781
55913
  appearance="ghost"
55782
55914
  content-hidden
55915
+ ?disabled="${x => x.disabled}"
55783
55916
  slot="start"
55784
55917
  title="Italics"
55785
55918
  @click=${x => x.italicsButtonClick()}
@@ -55793,6 +55926,7 @@ img.ProseMirror-separator {
55793
55926
  ${ref('bulletListButton')}
55794
55927
  appearance="ghost"
55795
55928
  content-hidden
55929
+ ?disabled="${x => x.disabled}"
55796
55930
  slot="start"
55797
55931
  title="Bullet List"
55798
55932
  @click=${x => x.bulletListButtonClick()}
@@ -55806,6 +55940,7 @@ img.ProseMirror-separator {
55806
55940
  ${ref('numberedListButton')}
55807
55941
  appearance="ghost"
55808
55942
  content-hidden
55943
+ ?disabled="${x => x.disabled}"
55809
55944
  slot="start"
55810
55945
  title="Numbered List"
55811
55946
  @click=${x => x.numberedListButtonClick()}
@@ -55820,12 +55955,14 @@ img.ProseMirror-separator {
55820
55955
  <slot name="footer-actions"></slot>
55821
55956
  </span>
55822
55957
  </section>
55958
+ ${errorTextTemplate}
55823
55959
  </div>
55824
55960
  </template>
55825
55961
  `;
55826
55962
 
55827
55963
  const styles$p = css `
55828
55964
  ${display('inline-flex')}
55965
+ ${styles$B}
55829
55966
 
55830
55967
  :host {
55831
55968
  font: ${bodyFont};
@@ -55835,6 +55972,9 @@ img.ProseMirror-separator {
55835
55972
  --ni-private-rich-text-editor-hover-indicator-width: calc(
55836
55973
  ${borderWidth} + 1px
55837
55974
  );
55975
+ ${
55976
+ /** Initial height of rich text editor with one line space when the footer is visible. */ ''}
55977
+ height: 82px;
55838
55978
  --ni-private-rich-text-editor-footer-section-height: 40px;
55839
55979
  ${
55840
55980
  /** Minimum width is added to accommodate all the possible buttons in the toolbar and to support the mobile width. */ ''}
@@ -55842,6 +55982,7 @@ img.ProseMirror-separator {
55842
55982
  }
55843
55983
 
55844
55984
  .container {
55985
+ box-sizing: border-box;
55845
55986
  display: flex;
55846
55987
  flex-direction: column;
55847
55988
  position: relative;
@@ -55873,37 +56014,55 @@ img.ProseMirror-separator {
55873
56014
  }
55874
56015
  }
55875
56016
 
56017
+ :host([disabled]) .container {
56018
+ color: ${bodyDisabledFontColor};
56019
+ border: ${borderWidth} solid rgba(${borderRgbPartialColor}, 0.1);
56020
+ }
56021
+
56022
+ :host([error-visible]) .container {
56023
+ border-bottom-color: ${failColor};
56024
+ }
56025
+
55876
56026
  :host(:hover) .container::after {
55877
- width: 100%;
56027
+ width: calc(100% + 2 * ${borderWidth});
56028
+ }
56029
+
56030
+ :host([disabled]:hover) .container::after {
56031
+ width: 0px;
56032
+ }
56033
+
56034
+ :host([error-visible]) .container::after {
56035
+ border-bottom-color: ${failColor};
56036
+ }
56037
+
56038
+ .editor-container {
56039
+ display: contents;
55878
56040
  }
55879
56041
 
55880
56042
  .editor {
56043
+ display: flex;
56044
+ flex-direction: column;
55881
56045
  border: ${borderWidth} solid transparent;
55882
56046
  border-radius: 0px;
55883
- height: calc(
55884
- 100% - var(--ni-private-rich-text-editor-footer-section-height)
55885
- );
55886
- overflow: auto;
56047
+ flex: 1;
56048
+ overflow: hidden;
55887
56049
  }
55888
56050
 
55889
- .editor-container {
55890
- display: contents;
56051
+ :host([footer-hidden]) .editor {
56052
+ height: 100%;
55891
56053
  }
55892
56054
 
55893
56055
  .ProseMirror {
55894
- ${
55895
- /**
55896
- * Min height represents the one line space for the initial view and max height is referred from the visual design.
55897
- * However, max height will be `fit-content` when the `fit-to-content` attribute for the editor component is implemented.
55898
- */ ''}
55899
- min-height: 32px;
55900
- max-height: 132px;
56056
+ overflow: auto;
55901
56057
  height: 100%;
55902
- border: ${borderWidth} solid transparent;
56058
+ border: 0px;
55903
56059
  border-radius: 0px;
55904
56060
  background-color: transparent;
55905
56061
  font: inherit;
55906
56062
  padding: 8px;
56063
+ ${
56064
+ /* This padding ensures that showing/hiding the error icon doesn't affect text layout */ ''}
56065
+ padding-right: calc(${iconSize});
55907
56066
  box-sizing: border-box;
55908
56067
  position: relative;
55909
56068
  color: inherit;
@@ -55950,15 +56109,38 @@ img.ProseMirror-separator {
55950
56109
  margin-block: 0;
55951
56110
  }
55952
56111
 
56112
+ ${
56113
+ /**
56114
+ * Styles provided by Tiptap are necessary to display the placeholder value when the editor is empty.
56115
+ * Tiptap doc reference: https://tiptap.dev/api/extensions/placeholder#additional-setup
56116
+ */ ''}
56117
+ .ProseMirror p.is-editor-empty:first-child::before {
56118
+ color: ${controlLabelFontColor};
56119
+ content: attr(data-placeholder);
56120
+ float: left;
56121
+ height: 0;
56122
+ pointer-events: none;
56123
+ word-break: break-word;
56124
+ }
56125
+
56126
+ :host([disabled]) .ProseMirror p.is-editor-empty:first-child::before {
56127
+ color: ${controlLabelDisabledFontColor};
56128
+ }
56129
+
55953
56130
  .footer-section {
55954
56131
  display: flex;
55955
56132
  justify-content: space-between;
56133
+ flex-shrink: 0;
55956
56134
  border: ${borderWidth} solid transparent;
55957
56135
  border-top-color: rgba(${borderRgbPartialColor}, 0.1);
55958
56136
  height: var(--ni-private-rich-text-editor-footer-section-height);
55959
56137
  overflow: hidden;
55960
56138
  }
55961
56139
 
56140
+ :host([footer-hidden]) .footer-section {
56141
+ display: none;
56142
+ }
56143
+
55962
56144
  nimble-toolbar::part(positioning-region) {
55963
56145
  background: transparent;
55964
56146
  padding-right: 8px;
@@ -55975,6 +56157,17 @@ img.ProseMirror-separator {
55975
56157
  gap: ${standardPadding};
55976
56158
  place-items: center;
55977
56159
  }
56160
+
56161
+ :host([error-visible]) .error-icon {
56162
+ display: none;
56163
+ }
56164
+
56165
+ :host([error-visible]) .error-icon.scrollbar-width-calculated {
56166
+ display: inline-flex;
56167
+ position: absolute;
56168
+ top: calc(${standardPadding} / 2);
56169
+ right: var(--ni-private-rich-text-editor-scrollbar-width);
56170
+ }
55978
56171
  `;
55979
56172
 
55980
56173
  /**
@@ -55982,12 +56175,57 @@ img.ProseMirror-separator {
55982
56175
  */
55983
56176
  class RichTextEditor extends FoundationElement {
55984
56177
  constructor() {
55985
- super();
56178
+ super(...arguments);
56179
+ /**
56180
+ * @internal
56181
+ */
56182
+ this.editor = this.createEditor();
56183
+ /**
56184
+ * @internal
56185
+ */
56186
+ this.tiptapEditor = this.createTiptapEditor();
56187
+ /**
56188
+ * Whether to disable user from editing and interacting with toolbar buttons
56189
+ *
56190
+ * @public
56191
+ * HTML Attribute: disabled
56192
+ */
56193
+ this.disabled = false;
56194
+ /**
56195
+ * Whether to hide the footer of the rich text editor
56196
+ *
56197
+ * @public
56198
+ * HTML Attribute: footer-hidden
56199
+ */
56200
+ this.footerHidden = false;
56201
+ /**
56202
+ * Whether to display the error state.
56203
+ *
56204
+ * @public
56205
+ * HTML Attribute: error-visible
56206
+ */
56207
+ this.errorVisible = false;
56208
+ /**
56209
+ * The width of the vertical scrollbar, if displayed.
56210
+ * @internal
56211
+ */
56212
+ this.scrollbarWidth = -1;
56213
+ this.updateScrollbarWidthQueued = false;
55986
56214
  this.markdownParser = this.initializeMarkdownParser();
55987
56215
  this.markdownSerializer = this.initializeMarkdownSerializer();
55988
56216
  this.domSerializer = DOMSerializer.fromSchema(schema);
55989
56217
  this.xmlSerializer = new XMLSerializer();
55990
- this.initializeEditor();
56218
+ }
56219
+ /**
56220
+ * True if the editor is empty or contains only whitespace, false otherwise.
56221
+ *
56222
+ * @public
56223
+ */
56224
+ get empty() {
56225
+ // Tiptap [isEmpty](https://tiptap.dev/api/editor#is-empty) returns false even if the editor has only whitespace.
56226
+ // However, the expectation is to return true if the editor is empty or contains only whitespace.
56227
+ // Hence, by retrieving the current text content using Tiptap state docs and then trimming the string to determine whether it is empty or not.
56228
+ return this.tiptapEditor.state.doc.textContent.trim().length === 0;
55991
56229
  }
55992
56230
  /**
55993
56231
  * @internal
@@ -55998,6 +56236,10 @@ img.ProseMirror-separator {
55998
56236
  this.editorContainer.append(this.editor);
55999
56237
  }
56000
56238
  this.bindEditorTransactionEvent();
56239
+ this.bindEditorUpdateEvent();
56240
+ this.stopNativeInputEventPropagation();
56241
+ this.resizeObserver = new ResizeObserver(() => this.onResize());
56242
+ this.resizeObserver.observe(this);
56001
56243
  }
56002
56244
  /**
56003
56245
  * @internal
@@ -56005,6 +56247,38 @@ img.ProseMirror-separator {
56005
56247
  disconnectedCallback() {
56006
56248
  super.disconnectedCallback();
56007
56249
  this.unbindEditorTransactionEvent();
56250
+ this.unbindEditorUpdateEvent();
56251
+ this.unbindNativeInputEvent();
56252
+ this.resizeObserver?.disconnect();
56253
+ }
56254
+ /**
56255
+ * @internal
56256
+ */
56257
+ disabledChanged() {
56258
+ this.tiptapEditor.setEditable(!this.disabled);
56259
+ this.setEditorTabIndex();
56260
+ this.editor.setAttribute('aria-disabled', this.disabled ? 'true' : 'false');
56261
+ }
56262
+ /**
56263
+ * Update the placeholder text and view of the editor.
56264
+ * @internal
56265
+ */
56266
+ placeholderChanged() {
56267
+ const placeholderExtension = this.getTipTapExtension('placeholder');
56268
+ placeholderExtension.options.placeholder = this.placeholder ?? '';
56269
+ this.tiptapEditor.view.dispatch(this.tiptapEditor.state.tr);
56270
+ this.queueUpdateScrollbarWidth();
56271
+ }
56272
+ /**
56273
+ * @internal
56274
+ */
56275
+ ariaLabelChanged() {
56276
+ if (this.ariaLabel !== null && this.ariaLabel !== undefined) {
56277
+ this.editor.setAttribute('aria-label', this.ariaLabel);
56278
+ }
56279
+ else {
56280
+ this.editor.removeAttribute('aria-label');
56281
+ }
56008
56282
  }
56009
56283
  /**
56010
56284
  * Toggle the bold mark and focus back to the editor
@@ -56012,6 +56286,7 @@ img.ProseMirror-separator {
56012
56286
  */
56013
56287
  boldButtonClick() {
56014
56288
  this.tiptapEditor.chain().focus().toggleBold().run();
56289
+ this.forceFocusEditor();
56015
56290
  }
56016
56291
  /**
56017
56292
  * Toggle the bold mark and focus back to the editor
@@ -56020,6 +56295,7 @@ img.ProseMirror-separator {
56020
56295
  boldButtonKeyDown(event) {
56021
56296
  if (this.keyActivatesButton(event)) {
56022
56297
  this.tiptapEditor.chain().focus().toggleBold().run();
56298
+ this.forceFocusEditor();
56023
56299
  return false;
56024
56300
  }
56025
56301
  return true;
@@ -56030,6 +56306,7 @@ img.ProseMirror-separator {
56030
56306
  */
56031
56307
  italicsButtonClick() {
56032
56308
  this.tiptapEditor.chain().focus().toggleItalic().run();
56309
+ this.forceFocusEditor();
56033
56310
  }
56034
56311
  /**
56035
56312
  * Toggle the italics mark and focus back to the editor
@@ -56038,6 +56315,7 @@ img.ProseMirror-separator {
56038
56315
  italicsButtonKeyDown(event) {
56039
56316
  if (this.keyActivatesButton(event)) {
56040
56317
  this.tiptapEditor.chain().focus().toggleItalic().run();
56318
+ this.forceFocusEditor();
56041
56319
  return false;
56042
56320
  }
56043
56321
  return true;
@@ -56048,6 +56326,7 @@ img.ProseMirror-separator {
56048
56326
  */
56049
56327
  bulletListButtonClick() {
56050
56328
  this.tiptapEditor.chain().focus().toggleBulletList().run();
56329
+ this.forceFocusEditor();
56051
56330
  }
56052
56331
  /**
56053
56332
  * Toggle the unordered list node and focus back to the editor
@@ -56056,6 +56335,7 @@ img.ProseMirror-separator {
56056
56335
  bulletListButtonKeyDown(event) {
56057
56336
  if (this.keyActivatesButton(event)) {
56058
56337
  this.tiptapEditor.chain().focus().toggleBulletList().run();
56338
+ this.forceFocusEditor();
56059
56339
  return false;
56060
56340
  }
56061
56341
  return true;
@@ -56066,6 +56346,7 @@ img.ProseMirror-separator {
56066
56346
  */
56067
56347
  numberedListButtonClick() {
56068
56348
  this.tiptapEditor.chain().focus().toggleOrderedList().run();
56349
+ this.forceFocusEditor();
56069
56350
  }
56070
56351
  /**
56071
56352
  * Toggle the ordered list node and focus back to the editor
@@ -56074,6 +56355,7 @@ img.ProseMirror-separator {
56074
56355
  numberedListButtonKeyDown(event) {
56075
56356
  if (this.keyActivatesButton(event)) {
56076
56357
  this.tiptapEditor.chain().focus().toggleOrderedList().run();
56358
+ this.forceFocusEditor();
56077
56359
  return false;
56078
56360
  }
56079
56361
  return true;
@@ -56103,6 +56385,39 @@ img.ProseMirror-separator {
56103
56385
  event.stopPropagation();
56104
56386
  return false;
56105
56387
  }
56388
+ createEditor() {
56389
+ const editor = document.createElement('div');
56390
+ editor.className = 'editor';
56391
+ editor.setAttribute('aria-multiline', 'true');
56392
+ editor.setAttribute('role', 'textbox');
56393
+ editor.setAttribute('aria-disabled', 'false');
56394
+ return editor;
56395
+ }
56396
+ createTiptapEditor() {
56397
+ /**
56398
+ * For more information on the extensions for the supported formatting options, refer to the links below.
56399
+ * Tiptap marks: https://tiptap.dev/api/marks
56400
+ * Tiptap nodes: https://tiptap.dev/api/nodes
56401
+ */
56402
+ return new Editor({
56403
+ element: this.editor,
56404
+ extensions: [
56405
+ Document$1,
56406
+ Paragraph,
56407
+ Text,
56408
+ BulletList,
56409
+ OrderedList,
56410
+ ListItem$1,
56411
+ Bold,
56412
+ Italic,
56413
+ History,
56414
+ Placeholder.configure({
56415
+ placeholder: '',
56416
+ showOnlyWhenEditable: false
56417
+ })
56418
+ ]
56419
+ });
56420
+ }
56106
56421
  /**
56107
56422
  * This function takes the Fragment from parseMarkdownToDOM function and return the serialized string using XMLSerializer
56108
56423
  */
@@ -56169,32 +56484,6 @@ img.ProseMirror-separator {
56169
56484
  }
56170
56485
  return this.domSerializer.serializeFragment(parsedMarkdownContent.content);
56171
56486
  }
56172
- initializeEditor() {
56173
- // Create div from the constructor because the TipTap editor requires its host element before the template is instantiated.
56174
- this.editor = document.createElement('div');
56175
- this.editor.className = 'editor';
56176
- this.editor.setAttribute('aria-multiline', 'true');
56177
- this.editor.setAttribute('role', 'textbox');
56178
- /**
56179
- * For more information on the extensions for the supported formatting options, refer to the links below.
56180
- * Tiptap marks: https://tiptap.dev/api/marks
56181
- * Tiptap nodes: https://tiptap.dev/api/nodes
56182
- */
56183
- this.tiptapEditor = new Editor({
56184
- element: this.editor,
56185
- extensions: [
56186
- Document$1,
56187
- Paragraph,
56188
- Text,
56189
- BulletList,
56190
- OrderedList,
56191
- ListItem$1,
56192
- Bold,
56193
- Italic,
56194
- History
56195
- ]
56196
- });
56197
- }
56198
56487
  /**
56199
56488
  * Binding the "transaction" event to the editor allows continuous monitoring the events and updating the button state in response to
56200
56489
  * various actions such as mouse events, keyboard events, changes in the editor content etc,.
@@ -56223,7 +56512,90 @@ img.ProseMirror-separator {
56223
56512
  return false;
56224
56513
  }
56225
56514
  }
56515
+ unbindEditorUpdateEvent() {
56516
+ this.tiptapEditor.off('update');
56517
+ }
56518
+ /**
56519
+ * input event is fired when there is a change in the content of the editor.
56520
+ *
56521
+ * https://tiptap.dev/api/events#update
56522
+ */
56523
+ bindEditorUpdateEvent() {
56524
+ this.tiptapEditor.on('update', () => {
56525
+ this.$emit('input');
56526
+ this.queueUpdateScrollbarWidth();
56527
+ });
56528
+ }
56529
+ /**
56530
+ * Stopping the native input event propagation emitted by the contenteditable element in the Tiptap
56531
+ * since there is an issue (linked below) in ProseMirror where selecting the text and removing it
56532
+ * does not trigger the native HTMLElement input event. So using the "update" event emitted by the
56533
+ * Tiptap to capture it as an "input" customEvent in the rich text editor.
56534
+ *
56535
+ * Prose Mirror issue: https://discuss.prosemirror.net/t/how-to-handle-select-backspace-delete-cut-type-kind-of-events-handletextinput-or-handledomevents-input-doesnt-help/4844
56536
+ */
56537
+ stopNativeInputEventPropagation() {
56538
+ this.tiptapEditor.view.dom.addEventListener('input', event => {
56539
+ event.stopPropagation();
56540
+ });
56541
+ }
56542
+ unbindNativeInputEvent() {
56543
+ this.tiptapEditor.view.dom.removeEventListener('input', () => { });
56544
+ }
56545
+ queueUpdateScrollbarWidth() {
56546
+ if (!this.$fastController.isConnected) {
56547
+ return;
56548
+ }
56549
+ if (!this.updateScrollbarWidthQueued) {
56550
+ this.updateScrollbarWidthQueued = true;
56551
+ DOM.queueUpdate(() => this.updateScrollbarWidth());
56552
+ }
56553
+ }
56554
+ updateScrollbarWidth() {
56555
+ this.updateScrollbarWidthQueued = false;
56556
+ this.scrollbarWidth = this.tiptapEditor.view.dom.offsetWidth
56557
+ - this.tiptapEditor.view.dom.clientWidth;
56558
+ }
56559
+ onResize() {
56560
+ this.scrollbarWidth = this.tiptapEditor.view.dom.offsetWidth
56561
+ - this.tiptapEditor.view.dom.clientWidth;
56562
+ }
56563
+ getTipTapExtension(extensionName) {
56564
+ return this.tiptapEditor.extensionManager.extensions.find(extension => extension.name === extensionName);
56565
+ }
56566
+ setEditorTabIndex() {
56567
+ this.tiptapEditor.setOptions({
56568
+ editorProps: {
56569
+ attributes: {
56570
+ tabindex: this.disabled ? '-1' : '0'
56571
+ }
56572
+ }
56573
+ });
56574
+ }
56575
+ // In Firefox browser, once the editor gets focused, the blinking caret will be visible until we click format buttons (Bold, Italic ...) in the Firefox browser (changing focus).
56576
+ // But once any of the toolbar button is clicked, editor internally has its focus but the blinking caret disappears.
56577
+ // As a workaround, manually triggering blur and setting focus on editor makes the blinking caret to re-appear.
56578
+ // Mozilla issue https://bugzilla.mozilla.org/show_bug.cgi?id=1496769 tracks removal of this workaround.
56579
+ forceFocusEditor() {
56580
+ this.tiptapEditor.commands.blur();
56581
+ this.tiptapEditor.commands.focus();
56582
+ }
56226
56583
  }
56584
+ __decorate$1([
56585
+ attr({ mode: 'boolean' })
56586
+ ], RichTextEditor.prototype, "disabled", void 0);
56587
+ __decorate$1([
56588
+ attr({ attribute: 'footer-hidden', mode: 'boolean' })
56589
+ ], RichTextEditor.prototype, "footerHidden", void 0);
56590
+ __decorate$1([
56591
+ attr({ attribute: 'error-visible', mode: 'boolean' })
56592
+ ], RichTextEditor.prototype, "errorVisible", void 0);
56593
+ __decorate$1([
56594
+ attr({ attribute: 'error-text' })
56595
+ ], RichTextEditor.prototype, "errorText", void 0);
56596
+ __decorate$1([
56597
+ attr
56598
+ ], RichTextEditor.prototype, "placeholder", void 0);
56227
56599
  __decorate$1([
56228
56600
  observable
56229
56601
  ], RichTextEditor.prototype, "boldButton", void 0);
@@ -56236,6 +56608,10 @@ img.ProseMirror-separator {
56236
56608
  __decorate$1([
56237
56609
  observable
56238
56610
  ], RichTextEditor.prototype, "numberedListButton", void 0);
56611
+ __decorate$1([
56612
+ observable
56613
+ ], RichTextEditor.prototype, "scrollbarWidth", void 0);
56614
+ applyMixins(RichTextEditor, ARIAGlobalStatesAndProperties);
56239
56615
  const nimbleRichTextEditor = RichTextEditor.compose({
56240
56616
  baseName: 'rich-text-editor',
56241
56617
  template: template$l,