e2ac898619bd69f589fa5c243f67e0b4a7822a366c765b6d2c7578df8a141b7997f0e2f3da3010e5a23c3f4305b4094097ff7e6ae7531b56b63ef637512b8b 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. /*---------------------------------------------------------------------------------------------
  2. * Copyright (c) Microsoft Corporation. All rights reserved.
  3. * Licensed under the MIT License. See License.txt in the project root for license information.
  4. *--------------------------------------------------------------------------------------------*/
  5. import './iconlabel.css';
  6. import * as dom from '../../dom.js';
  7. import { HighlightedLabel } from '../highlightedlabel/highlightedLabel.js';
  8. import { setupCustomHover, setupNativeHover } from './iconLabelHover.js';
  9. import { Disposable } from '../../../common/lifecycle.js';
  10. import { equals } from '../../../common/objects.js';
  11. import { Range } from '../../../common/range.js';
  12. class FastLabelNode {
  13. constructor(_element) {
  14. this._element = _element;
  15. }
  16. get element() {
  17. return this._element;
  18. }
  19. set textContent(content) {
  20. if (this.disposed || content === this._textContent) {
  21. return;
  22. }
  23. this._textContent = content;
  24. this._element.textContent = content;
  25. }
  26. set className(className) {
  27. if (this.disposed || className === this._className) {
  28. return;
  29. }
  30. this._className = className;
  31. this._element.className = className;
  32. }
  33. set empty(empty) {
  34. if (this.disposed || empty === this._empty) {
  35. return;
  36. }
  37. this._empty = empty;
  38. this._element.style.marginLeft = empty ? '0' : '';
  39. }
  40. dispose() {
  41. this.disposed = true;
  42. }
  43. }
  44. export class IconLabel extends Disposable {
  45. constructor(container, options) {
  46. super();
  47. this.customHovers = new Map();
  48. this.domNode = this._register(new FastLabelNode(dom.append(container, dom.$('.monaco-icon-label'))));
  49. this.labelContainer = dom.append(this.domNode.element, dom.$('.monaco-icon-label-container'));
  50. const nameContainer = dom.append(this.labelContainer, dom.$('span.monaco-icon-name-container'));
  51. this.descriptionContainer = this._register(new FastLabelNode(dom.append(this.labelContainer, dom.$('span.monaco-icon-description-container'))));
  52. if ((options === null || options === void 0 ? void 0 : options.supportHighlights) || (options === null || options === void 0 ? void 0 : options.supportIcons)) {
  53. this.nameNode = new LabelWithHighlights(nameContainer, !!options.supportIcons);
  54. }
  55. else {
  56. this.nameNode = new Label(nameContainer);
  57. }
  58. if (options === null || options === void 0 ? void 0 : options.supportDescriptionHighlights) {
  59. this.descriptionNodeFactory = () => new HighlightedLabel(dom.append(this.descriptionContainer.element, dom.$('span.label-description')), { supportIcons: !!options.supportIcons });
  60. }
  61. else {
  62. this.descriptionNodeFactory = () => this._register(new FastLabelNode(dom.append(this.descriptionContainer.element, dom.$('span.label-description'))));
  63. }
  64. this.hoverDelegate = options === null || options === void 0 ? void 0 : options.hoverDelegate;
  65. }
  66. get element() {
  67. return this.domNode.element;
  68. }
  69. setLabel(label, description, options) {
  70. const classes = ['monaco-icon-label'];
  71. if (options) {
  72. if (options.extraClasses) {
  73. classes.push(...options.extraClasses);
  74. }
  75. if (options.italic) {
  76. classes.push('italic');
  77. }
  78. if (options.strikethrough) {
  79. classes.push('strikethrough');
  80. }
  81. }
  82. this.domNode.className = classes.join(' ');
  83. this.setupHover((options === null || options === void 0 ? void 0 : options.descriptionTitle) ? this.labelContainer : this.element, options === null || options === void 0 ? void 0 : options.title);
  84. this.nameNode.setLabel(label, options);
  85. if (description || this.descriptionNode) {
  86. if (!this.descriptionNode) {
  87. this.descriptionNode = this.descriptionNodeFactory(); // description node is created lazily on demand
  88. }
  89. if (this.descriptionNode instanceof HighlightedLabel) {
  90. this.descriptionNode.set(description || '', options ? options.descriptionMatches : undefined);
  91. this.setupHover(this.descriptionNode.element, options === null || options === void 0 ? void 0 : options.descriptionTitle);
  92. }
  93. else {
  94. this.descriptionNode.textContent = description || '';
  95. this.setupHover(this.descriptionNode.element, (options === null || options === void 0 ? void 0 : options.descriptionTitle) || '');
  96. this.descriptionNode.empty = !description;
  97. }
  98. }
  99. }
  100. setupHover(htmlElement, tooltip) {
  101. const previousCustomHover = this.customHovers.get(htmlElement);
  102. if (previousCustomHover) {
  103. previousCustomHover.dispose();
  104. this.customHovers.delete(htmlElement);
  105. }
  106. if (!tooltip) {
  107. htmlElement.removeAttribute('title');
  108. return;
  109. }
  110. if (!this.hoverDelegate) {
  111. setupNativeHover(htmlElement, tooltip);
  112. }
  113. else {
  114. const hoverDisposable = setupCustomHover(this.hoverDelegate, htmlElement, tooltip);
  115. if (hoverDisposable) {
  116. this.customHovers.set(htmlElement, hoverDisposable);
  117. }
  118. }
  119. }
  120. dispose() {
  121. super.dispose();
  122. for (const disposable of this.customHovers.values()) {
  123. disposable.dispose();
  124. }
  125. this.customHovers.clear();
  126. }
  127. }
  128. class Label {
  129. constructor(container) {
  130. this.container = container;
  131. this.label = undefined;
  132. this.singleLabel = undefined;
  133. }
  134. setLabel(label, options) {
  135. if (this.label === label && equals(this.options, options)) {
  136. return;
  137. }
  138. this.label = label;
  139. this.options = options;
  140. if (typeof label === 'string') {
  141. if (!this.singleLabel) {
  142. this.container.innerText = '';
  143. this.container.classList.remove('multiple');
  144. this.singleLabel = dom.append(this.container, dom.$('a.label-name', { id: options === null || options === void 0 ? void 0 : options.domId }));
  145. }
  146. this.singleLabel.textContent = label;
  147. }
  148. else {
  149. this.container.innerText = '';
  150. this.container.classList.add('multiple');
  151. this.singleLabel = undefined;
  152. for (let i = 0; i < label.length; i++) {
  153. const l = label[i];
  154. const id = (options === null || options === void 0 ? void 0 : options.domId) && `${options === null || options === void 0 ? void 0 : options.domId}_${i}`;
  155. dom.append(this.container, dom.$('a.label-name', { id, 'data-icon-label-count': label.length, 'data-icon-label-index': i, 'role': 'treeitem' }, l));
  156. if (i < label.length - 1) {
  157. dom.append(this.container, dom.$('span.label-separator', undefined, (options === null || options === void 0 ? void 0 : options.separator) || '/'));
  158. }
  159. }
  160. }
  161. }
  162. }
  163. function splitMatches(labels, separator, matches) {
  164. if (!matches) {
  165. return undefined;
  166. }
  167. let labelStart = 0;
  168. return labels.map(label => {
  169. const labelRange = { start: labelStart, end: labelStart + label.length };
  170. const result = matches
  171. .map(match => Range.intersect(labelRange, match))
  172. .filter(range => !Range.isEmpty(range))
  173. .map(({ start, end }) => ({ start: start - labelStart, end: end - labelStart }));
  174. labelStart = labelRange.end + separator.length;
  175. return result;
  176. });
  177. }
  178. class LabelWithHighlights {
  179. constructor(container, supportIcons) {
  180. this.container = container;
  181. this.supportIcons = supportIcons;
  182. this.label = undefined;
  183. this.singleLabel = undefined;
  184. }
  185. setLabel(label, options) {
  186. if (this.label === label && equals(this.options, options)) {
  187. return;
  188. }
  189. this.label = label;
  190. this.options = options;
  191. if (typeof label === 'string') {
  192. if (!this.singleLabel) {
  193. this.container.innerText = '';
  194. this.container.classList.remove('multiple');
  195. this.singleLabel = new HighlightedLabel(dom.append(this.container, dom.$('a.label-name', { id: options === null || options === void 0 ? void 0 : options.domId })), { supportIcons: this.supportIcons });
  196. }
  197. this.singleLabel.set(label, options === null || options === void 0 ? void 0 : options.matches, undefined, options === null || options === void 0 ? void 0 : options.labelEscapeNewLines);
  198. }
  199. else {
  200. this.container.innerText = '';
  201. this.container.classList.add('multiple');
  202. this.singleLabel = undefined;
  203. const separator = (options === null || options === void 0 ? void 0 : options.separator) || '/';
  204. const matches = splitMatches(label, separator, options === null || options === void 0 ? void 0 : options.matches);
  205. for (let i = 0; i < label.length; i++) {
  206. const l = label[i];
  207. const m = matches ? matches[i] : undefined;
  208. const id = (options === null || options === void 0 ? void 0 : options.domId) && `${options === null || options === void 0 ? void 0 : options.domId}_${i}`;
  209. const name = dom.$('a.label-name', { id, 'data-icon-label-count': label.length, 'data-icon-label-index': i, 'role': 'treeitem' });
  210. const highlightedLabel = new HighlightedLabel(dom.append(this.container, name), { supportIcons: this.supportIcons });
  211. highlightedLabel.set(l, m, undefined, options === null || options === void 0 ? void 0 : options.labelEscapeNewLines);
  212. if (i < label.length - 1) {
  213. dom.append(name, dom.$('span.label-separator', undefined, separator));
  214. }
  215. }
  216. }
  217. }
  218. }