dropdown-item.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470
  1. /*!
  2. * All material copyright ESRI, All Rights Reserved, unless otherwise specified.
  3. * See https://github.com/Esri/calcite-components/blob/master/LICENSE.md for details.
  4. * v1.0.0-beta.97
  5. */
  6. import { h, Host } from "@stencil/core";
  7. import { getElementProp, toAriaBoolean } from "../../utils/dom";
  8. import { CSS } from "./resources";
  9. /**
  10. * @slot - A slot for adding text.
  11. */
  12. export class DropdownItem {
  13. constructor() {
  14. //--------------------------------------------------------------------------
  15. //
  16. // Public Properties
  17. //
  18. //--------------------------------------------------------------------------
  19. /**
  20. * Indicates whether the item is active.
  21. *
  22. * @deprecated Use selected instead.
  23. */
  24. this.active = false;
  25. /** When true, item is selected */
  26. this.selected = false;
  27. }
  28. activeHandler(value) {
  29. this.selected = value;
  30. }
  31. selectedHandler(value) {
  32. this.active = value;
  33. }
  34. //--------------------------------------------------------------------------
  35. //
  36. // Public Methods
  37. //
  38. //--------------------------------------------------------------------------
  39. /** Sets focus on the component. */
  40. async setFocus() {
  41. var _a;
  42. (_a = this.el) === null || _a === void 0 ? void 0 : _a.focus();
  43. }
  44. //--------------------------------------------------------------------------
  45. //
  46. // Lifecycle
  47. //
  48. //--------------------------------------------------------------------------
  49. componentWillLoad() {
  50. this.initialize();
  51. }
  52. connectedCallback() {
  53. const isSelected = this.selected || this.active;
  54. if (isSelected) {
  55. this.activeHandler(isSelected);
  56. this.selectedHandler(isSelected);
  57. }
  58. this.initialize();
  59. }
  60. render() {
  61. const scale = getElementProp(this.el, "scale", "m");
  62. const iconStartEl = (h("calcite-icon", { class: "dropdown-item-icon-start", flipRtl: this.iconFlipRtl === "start" || this.iconFlipRtl === "both", icon: this.iconStart, scale: "s" }));
  63. const contentNode = (h("span", { class: "dropdown-item-content" }, h("slot", null)));
  64. const iconEndEl = (h("calcite-icon", { class: "dropdown-item-icon-end", flipRtl: this.iconFlipRtl === "end" || this.iconFlipRtl === "both", icon: this.iconEnd, scale: "s" }));
  65. const slottedContent = this.iconStart && this.iconEnd
  66. ? [iconStartEl, contentNode, iconEndEl]
  67. : this.iconStart
  68. ? [iconStartEl, h("slot", null)]
  69. : this.iconEnd
  70. ? [contentNode, iconEndEl]
  71. : contentNode;
  72. const contentEl = !this.href ? (slottedContent) : (h("a", { "aria-label": this.label, class: "dropdown-link", href: this.href, ref: (el) => (this.childLink = el), rel: this.rel, target: this.target }, slottedContent));
  73. const itemRole = this.href
  74. ? null
  75. : this.selectionMode === "single"
  76. ? "menuitemradio"
  77. : this.selectionMode === "multi"
  78. ? "menuitemcheckbox"
  79. : "menuitem";
  80. const itemAria = this.selectionMode !== "none" ? toAriaBoolean(this.selected) : null;
  81. return (h(Host, { "aria-checked": itemAria, role: itemRole, tabindex: "0" }, h("div", { class: {
  82. container: true,
  83. [CSS.containerLink]: !!this.href,
  84. [CSS.containerSmall]: scale === "s",
  85. [CSS.containerMedium]: scale === "m",
  86. [CSS.containerLarge]: scale === "l",
  87. [CSS.containerMulti]: this.selectionMode === "multi",
  88. [CSS.containerSingle]: this.selectionMode === "single",
  89. [CSS.containerNone]: this.selectionMode === "none"
  90. } }, this.selectionMode !== "none" ? (h("calcite-icon", { class: "dropdown-item-icon", icon: this.selectionMode === "multi" ? "check" : "bullet-point", scale: "s" })) : null, contentEl)));
  91. }
  92. //--------------------------------------------------------------------------
  93. //
  94. // Event Listeners
  95. //
  96. //--------------------------------------------------------------------------
  97. onClick() {
  98. this.emitRequestedItem();
  99. }
  100. keyDownHandler(event) {
  101. switch (event.key) {
  102. case " ":
  103. case "Enter":
  104. this.emitRequestedItem();
  105. if (this.href) {
  106. this.childLink.click();
  107. }
  108. event.preventDefault();
  109. break;
  110. case "Escape":
  111. this.calciteInternalDropdownCloseRequest.emit();
  112. event.preventDefault();
  113. break;
  114. case "Tab":
  115. this.calciteInternalDropdownItemKeyEvent.emit({ keyboardEvent: event });
  116. break;
  117. case "ArrowUp":
  118. case "ArrowDown":
  119. case "Home":
  120. case "End":
  121. event.preventDefault();
  122. this.calciteInternalDropdownItemKeyEvent.emit({ keyboardEvent: event });
  123. break;
  124. }
  125. }
  126. updateActiveItemOnChange(event) {
  127. const parentEmittedChange = event.composedPath().includes(this.parentDropdownGroupEl);
  128. if (parentEmittedChange) {
  129. this.requestedDropdownGroup = event.detail.requestedDropdownGroup;
  130. this.requestedDropdownItem = event.detail.requestedDropdownItem;
  131. this.determineActiveItem();
  132. }
  133. event.stopPropagation();
  134. }
  135. //--------------------------------------------------------------------------
  136. //
  137. // Private Methods
  138. //
  139. //--------------------------------------------------------------------------
  140. initialize() {
  141. this.selectionMode = getElementProp(this.el, "selection-mode", "single");
  142. this.parentDropdownGroupEl = this.el.closest("calcite-dropdown-group");
  143. if (this.selectionMode === "none") {
  144. this.selected = false;
  145. }
  146. }
  147. determineActiveItem() {
  148. switch (this.selectionMode) {
  149. case "multi":
  150. if (this.el === this.requestedDropdownItem) {
  151. this.selected = !this.selected;
  152. }
  153. break;
  154. case "single":
  155. if (this.el === this.requestedDropdownItem) {
  156. this.selected = true;
  157. }
  158. else if (this.requestedDropdownGroup === this.parentDropdownGroupEl) {
  159. this.selected = false;
  160. }
  161. break;
  162. case "none":
  163. this.selected = false;
  164. break;
  165. }
  166. }
  167. emitRequestedItem() {
  168. this.calciteInternalDropdownItemSelect.emit({
  169. requestedDropdownItem: this.el,
  170. requestedDropdownGroup: this.parentDropdownGroupEl
  171. });
  172. }
  173. static get is() { return "calcite-dropdown-item"; }
  174. static get encapsulation() { return "shadow"; }
  175. static get originalStyleUrls() {
  176. return {
  177. "$": ["dropdown-item.scss"]
  178. };
  179. }
  180. static get styleUrls() {
  181. return {
  182. "$": ["dropdown-item.css"]
  183. };
  184. }
  185. static get properties() {
  186. return {
  187. "active": {
  188. "type": "boolean",
  189. "mutable": true,
  190. "complexType": {
  191. "original": "boolean",
  192. "resolved": "boolean",
  193. "references": {}
  194. },
  195. "required": false,
  196. "optional": false,
  197. "docs": {
  198. "tags": [{
  199. "name": "deprecated",
  200. "text": "Use selected instead."
  201. }],
  202. "text": "Indicates whether the item is active."
  203. },
  204. "attribute": "active",
  205. "reflect": true,
  206. "defaultValue": "false"
  207. },
  208. "selected": {
  209. "type": "boolean",
  210. "mutable": true,
  211. "complexType": {
  212. "original": "boolean",
  213. "resolved": "boolean",
  214. "references": {}
  215. },
  216. "required": false,
  217. "optional": false,
  218. "docs": {
  219. "tags": [],
  220. "text": "When true, item is selected"
  221. },
  222. "attribute": "selected",
  223. "reflect": true,
  224. "defaultValue": "false"
  225. },
  226. "iconFlipRtl": {
  227. "type": "string",
  228. "mutable": false,
  229. "complexType": {
  230. "original": "FlipContext",
  231. "resolved": "\"both\" | \"end\" | \"start\"",
  232. "references": {
  233. "FlipContext": {
  234. "location": "import",
  235. "path": "../interfaces"
  236. }
  237. }
  238. },
  239. "required": false,
  240. "optional": true,
  241. "docs": {
  242. "tags": [],
  243. "text": "When true, the icon will be flipped when the element direction is right-to-left (`\"rtl\"`)."
  244. },
  245. "attribute": "icon-flip-rtl",
  246. "reflect": true
  247. },
  248. "iconStart": {
  249. "type": "string",
  250. "mutable": false,
  251. "complexType": {
  252. "original": "string",
  253. "resolved": "string",
  254. "references": {}
  255. },
  256. "required": false,
  257. "optional": true,
  258. "docs": {
  259. "tags": [],
  260. "text": "Specifies an icon to display at the start of the component."
  261. },
  262. "attribute": "icon-start",
  263. "reflect": true
  264. },
  265. "iconEnd": {
  266. "type": "string",
  267. "mutable": false,
  268. "complexType": {
  269. "original": "string",
  270. "resolved": "string",
  271. "references": {}
  272. },
  273. "required": false,
  274. "optional": true,
  275. "docs": {
  276. "tags": [],
  277. "text": "Specifies an icon to display at the end of the component."
  278. },
  279. "attribute": "icon-end",
  280. "reflect": true
  281. },
  282. "href": {
  283. "type": "string",
  284. "mutable": false,
  285. "complexType": {
  286. "original": "string",
  287. "resolved": "string",
  288. "references": {}
  289. },
  290. "required": false,
  291. "optional": true,
  292. "docs": {
  293. "tags": [],
  294. "text": "optionally pass a href - used to determine if the component should render as anchor"
  295. },
  296. "attribute": "href",
  297. "reflect": true
  298. },
  299. "label": {
  300. "type": "string",
  301. "mutable": false,
  302. "complexType": {
  303. "original": "string",
  304. "resolved": "string",
  305. "references": {}
  306. },
  307. "required": false,
  308. "optional": true,
  309. "docs": {
  310. "tags": [],
  311. "text": "Applies to the aria-label attribute on the button or hyperlink"
  312. },
  313. "attribute": "label",
  314. "reflect": false
  315. },
  316. "rel": {
  317. "type": "string",
  318. "mutable": false,
  319. "complexType": {
  320. "original": "string",
  321. "resolved": "string",
  322. "references": {}
  323. },
  324. "required": false,
  325. "optional": true,
  326. "docs": {
  327. "tags": [],
  328. "text": "The rel attribute to apply to the hyperlink"
  329. },
  330. "attribute": "rel",
  331. "reflect": true
  332. },
  333. "target": {
  334. "type": "string",
  335. "mutable": false,
  336. "complexType": {
  337. "original": "string",
  338. "resolved": "string",
  339. "references": {}
  340. },
  341. "required": false,
  342. "optional": true,
  343. "docs": {
  344. "tags": [],
  345. "text": "The target attribute to apply to the hyperlink"
  346. },
  347. "attribute": "target",
  348. "reflect": true
  349. }
  350. };
  351. }
  352. static get events() {
  353. return [{
  354. "method": "calciteInternalDropdownItemSelect",
  355. "name": "calciteInternalDropdownItemSelect",
  356. "bubbles": true,
  357. "cancelable": false,
  358. "composed": true,
  359. "docs": {
  360. "tags": [{
  361. "name": "internal",
  362. "text": undefined
  363. }],
  364. "text": ""
  365. },
  366. "complexType": {
  367. "original": "RequestedItem",
  368. "resolved": "RequestedItem",
  369. "references": {
  370. "RequestedItem": {
  371. "location": "import",
  372. "path": "../dropdown-group/interfaces"
  373. }
  374. }
  375. }
  376. }, {
  377. "method": "calciteInternalDropdownItemKeyEvent",
  378. "name": "calciteInternalDropdownItemKeyEvent",
  379. "bubbles": true,
  380. "cancelable": false,
  381. "composed": true,
  382. "docs": {
  383. "tags": [{
  384. "name": "internal",
  385. "text": undefined
  386. }],
  387. "text": ""
  388. },
  389. "complexType": {
  390. "original": "ItemKeyboardEvent",
  391. "resolved": "ItemKeyboardEvent",
  392. "references": {
  393. "ItemKeyboardEvent": {
  394. "location": "import",
  395. "path": "../dropdown/interfaces"
  396. }
  397. }
  398. }
  399. }, {
  400. "method": "calciteInternalDropdownCloseRequest",
  401. "name": "calciteInternalDropdownCloseRequest",
  402. "bubbles": true,
  403. "cancelable": false,
  404. "composed": true,
  405. "docs": {
  406. "tags": [{
  407. "name": "internal",
  408. "text": undefined
  409. }],
  410. "text": ""
  411. },
  412. "complexType": {
  413. "original": "void",
  414. "resolved": "void",
  415. "references": {}
  416. }
  417. }];
  418. }
  419. static get methods() {
  420. return {
  421. "setFocus": {
  422. "complexType": {
  423. "signature": "() => Promise<void>",
  424. "parameters": [],
  425. "references": {
  426. "Promise": {
  427. "location": "global"
  428. }
  429. },
  430. "return": "Promise<void>"
  431. },
  432. "docs": {
  433. "text": "Sets focus on the component.",
  434. "tags": []
  435. }
  436. }
  437. };
  438. }
  439. static get elementRef() { return "el"; }
  440. static get watchers() {
  441. return [{
  442. "propName": "active",
  443. "methodName": "activeHandler"
  444. }, {
  445. "propName": "selected",
  446. "methodName": "selectedHandler"
  447. }];
  448. }
  449. static get listeners() {
  450. return [{
  451. "name": "click",
  452. "method": "onClick",
  453. "target": undefined,
  454. "capture": false,
  455. "passive": false
  456. }, {
  457. "name": "keydown",
  458. "method": "keyDownHandler",
  459. "target": undefined,
  460. "capture": false,
  461. "passive": false
  462. }, {
  463. "name": "calciteInternalDropdownItemChange",
  464. "method": "updateActiveItemOnChange",
  465. "target": "body",
  466. "capture": false,
  467. "passive": false
  468. }];
  469. }
  470. }