radio-group.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450
  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.82
  5. */
  6. import { Component, Event, h, Listen, Element, Prop, Watch, Host, Build, Method } from "@stencil/core";
  7. import { getElementDir } from "../../utils/dom";
  8. import { connectLabel, disconnectLabel } from "../../utils/label";
  9. import { connectForm, disconnectForm, HiddenFormInputSlot } from "../../utils/form";
  10. import { updateHostInteraction } from "../../utils/interactive";
  11. /**
  12. * @slot - A slot for adding `calcite-radio-group-item`s.
  13. */
  14. export class RadioGroup {
  15. constructor() {
  16. //--------------------------------------------------------------------------
  17. //
  18. // Properties
  19. //
  20. //--------------------------------------------------------------------------
  21. /** specify the appearance style of the radio group, defaults to solid. */
  22. this.appearance = "solid";
  23. /** is the radio group disabled */
  24. this.disabled = false;
  25. /**
  26. * When true, makes the component required for form-submission.
  27. *
  28. * @internal
  29. */
  30. this.required = false;
  31. /** specify the layout of the radio group, defaults to horizontal */
  32. this.layout = "horizontal";
  33. /** The scale of the radio group */
  34. this.scale = "m";
  35. /** The value of the selectedItem */
  36. this.value = null;
  37. /** specify the width of the group, defaults to auto */
  38. this.width = "auto";
  39. //--------------------------------------------------------------------------
  40. //
  41. // Event Listeners
  42. //
  43. //--------------------------------------------------------------------------
  44. this.handleClick = (event) => {
  45. if (event.target.localName === "calcite-radio-group-item") {
  46. this.selectItem(event.target, true);
  47. }
  48. };
  49. }
  50. valueHandler(value) {
  51. const items = this.getItems();
  52. items.forEach((item) => (item.checked = item.value === value));
  53. }
  54. handleSelectedItemChange(newItem, oldItem) {
  55. this.value = newItem === null || newItem === void 0 ? void 0 : newItem.value;
  56. if (newItem === oldItem) {
  57. return;
  58. }
  59. const items = this.getItems();
  60. const match = Array.from(items)
  61. .filter((item) => item === newItem)
  62. .pop();
  63. if (match) {
  64. this.selectItem(match);
  65. }
  66. else if (items[0]) {
  67. items[0].tabIndex = 0;
  68. }
  69. }
  70. //--------------------------------------------------------------------------
  71. //
  72. // Lifecycle
  73. //
  74. //--------------------------------------------------------------------------
  75. componentWillLoad() {
  76. const items = this.getItems();
  77. const lastChecked = Array.from(items)
  78. .filter((item) => item.checked)
  79. .pop();
  80. if (lastChecked) {
  81. this.selectItem(lastChecked);
  82. }
  83. else if (items[0]) {
  84. items[0].tabIndex = 0;
  85. }
  86. }
  87. connectedCallback() {
  88. connectLabel(this);
  89. connectForm(this);
  90. }
  91. disconnectedCallback() {
  92. disconnectLabel(this);
  93. disconnectForm(this);
  94. }
  95. componentDidRender() {
  96. updateHostInteraction(this);
  97. }
  98. render() {
  99. return (h(Host, { onClick: this.handleClick, role: "radiogroup" },
  100. h("slot", null),
  101. h(HiddenFormInputSlot, { component: this })));
  102. }
  103. handleSelected(event) {
  104. event.stopPropagation();
  105. event.preventDefault();
  106. this.selectItem(event.target);
  107. }
  108. handleKeyDown(event) {
  109. const keys = ["ArrowLeft", "ArrowUp", "ArrowRight", "ArrowDown", " "];
  110. const key = event.key;
  111. const { el, selectedItem } = this;
  112. if (keys.indexOf(key) === -1) {
  113. return;
  114. }
  115. let adjustedKey = key;
  116. if (getElementDir(el) === "rtl") {
  117. if (key === "ArrowRight") {
  118. adjustedKey = "ArrowLeft";
  119. }
  120. if (key === "ArrowLeft") {
  121. adjustedKey = "ArrowRight";
  122. }
  123. }
  124. const items = this.getItems();
  125. let selectedIndex = -1;
  126. items.forEach((item, index) => {
  127. if (item === selectedItem) {
  128. selectedIndex = index;
  129. }
  130. });
  131. switch (adjustedKey) {
  132. case "ArrowLeft":
  133. case "ArrowUp":
  134. event.preventDefault();
  135. const previous = selectedIndex < 1 ? items.item(items.length - 1) : items.item(selectedIndex - 1);
  136. this.selectItem(previous, true);
  137. return;
  138. case "ArrowRight":
  139. case "ArrowDown":
  140. event.preventDefault();
  141. const next = selectedIndex === -1 ? items.item(1) : items.item(selectedIndex + 1) || items.item(0);
  142. this.selectItem(next, true);
  143. return;
  144. case " ":
  145. event.preventDefault();
  146. this.selectItem(event.target, true);
  147. return;
  148. default:
  149. return;
  150. }
  151. }
  152. // --------------------------------------------------------------------------
  153. //
  154. // Methods
  155. //
  156. // --------------------------------------------------------------------------
  157. /** Sets focus on the component. */
  158. async setFocus() {
  159. var _a;
  160. (_a = (this.selectedItem || this.getItems()[0])) === null || _a === void 0 ? void 0 : _a.focus();
  161. }
  162. //--------------------------------------------------------------------------
  163. //
  164. // Private Methods
  165. //
  166. //--------------------------------------------------------------------------
  167. onLabelClick() {
  168. this.setFocus();
  169. }
  170. getItems() {
  171. return this.el.querySelectorAll("calcite-radio-group-item");
  172. }
  173. selectItem(selected, emit = false) {
  174. if (selected === this.selectedItem) {
  175. return;
  176. }
  177. const items = this.getItems();
  178. let match = null;
  179. items.forEach((item) => {
  180. const matches = item.value === selected.value;
  181. if ((matches && !item.checked) || (!matches && item.checked)) {
  182. item.checked = matches;
  183. }
  184. item.tabIndex = matches ? 0 : -1;
  185. if (matches) {
  186. match = item;
  187. if (emit) {
  188. this.calciteRadioGroupChange.emit(match.value);
  189. }
  190. }
  191. });
  192. this.selectedItem = match;
  193. if (Build.isBrowser && match) {
  194. match.focus();
  195. }
  196. }
  197. static get is() { return "calcite-radio-group"; }
  198. static get encapsulation() { return "shadow"; }
  199. static get originalStyleUrls() { return {
  200. "$": ["radio-group.scss"]
  201. }; }
  202. static get styleUrls() { return {
  203. "$": ["radio-group.css"]
  204. }; }
  205. static get properties() { return {
  206. "appearance": {
  207. "type": "string",
  208. "mutable": false,
  209. "complexType": {
  210. "original": "RadioAppearance",
  211. "resolved": "\"outline\" | \"solid\"",
  212. "references": {
  213. "RadioAppearance": {
  214. "location": "import",
  215. "path": "./interfaces"
  216. }
  217. }
  218. },
  219. "required": false,
  220. "optional": false,
  221. "docs": {
  222. "tags": [],
  223. "text": "specify the appearance style of the radio group, defaults to solid."
  224. },
  225. "attribute": "appearance",
  226. "reflect": true,
  227. "defaultValue": "\"solid\""
  228. },
  229. "disabled": {
  230. "type": "boolean",
  231. "mutable": false,
  232. "complexType": {
  233. "original": "boolean",
  234. "resolved": "boolean",
  235. "references": {}
  236. },
  237. "required": false,
  238. "optional": false,
  239. "docs": {
  240. "tags": [],
  241. "text": "is the radio group disabled"
  242. },
  243. "attribute": "disabled",
  244. "reflect": true,
  245. "defaultValue": "false"
  246. },
  247. "required": {
  248. "type": "boolean",
  249. "mutable": false,
  250. "complexType": {
  251. "original": "boolean",
  252. "resolved": "boolean",
  253. "references": {}
  254. },
  255. "required": false,
  256. "optional": false,
  257. "docs": {
  258. "tags": [{
  259. "name": "internal",
  260. "text": undefined
  261. }],
  262. "text": "When true, makes the component required for form-submission."
  263. },
  264. "attribute": "required",
  265. "reflect": true,
  266. "defaultValue": "false"
  267. },
  268. "layout": {
  269. "type": "string",
  270. "mutable": false,
  271. "complexType": {
  272. "original": "Layout",
  273. "resolved": "\"grid\" | \"horizontal\" | \"vertical\"",
  274. "references": {
  275. "Layout": {
  276. "location": "import",
  277. "path": "../interfaces"
  278. }
  279. }
  280. },
  281. "required": false,
  282. "optional": false,
  283. "docs": {
  284. "tags": [],
  285. "text": "specify the layout of the radio group, defaults to horizontal"
  286. },
  287. "attribute": "layout",
  288. "reflect": true,
  289. "defaultValue": "\"horizontal\""
  290. },
  291. "name": {
  292. "type": "string",
  293. "mutable": false,
  294. "complexType": {
  295. "original": "string",
  296. "resolved": "string",
  297. "references": {}
  298. },
  299. "required": false,
  300. "optional": false,
  301. "docs": {
  302. "tags": [],
  303. "text": "The group's name. Gets submitted with the form."
  304. },
  305. "attribute": "name",
  306. "reflect": false
  307. },
  308. "scale": {
  309. "type": "string",
  310. "mutable": false,
  311. "complexType": {
  312. "original": "Scale",
  313. "resolved": "\"l\" | \"m\" | \"s\"",
  314. "references": {
  315. "Scale": {
  316. "location": "import",
  317. "path": "../interfaces"
  318. }
  319. }
  320. },
  321. "required": false,
  322. "optional": false,
  323. "docs": {
  324. "tags": [],
  325. "text": "The scale of the radio group"
  326. },
  327. "attribute": "scale",
  328. "reflect": true,
  329. "defaultValue": "\"m\""
  330. },
  331. "value": {
  332. "type": "string",
  333. "mutable": true,
  334. "complexType": {
  335. "original": "string",
  336. "resolved": "string",
  337. "references": {}
  338. },
  339. "required": false,
  340. "optional": false,
  341. "docs": {
  342. "tags": [],
  343. "text": "The value of the selectedItem"
  344. },
  345. "attribute": "value",
  346. "reflect": false,
  347. "defaultValue": "null"
  348. },
  349. "selectedItem": {
  350. "type": "unknown",
  351. "mutable": true,
  352. "complexType": {
  353. "original": "HTMLCalciteRadioGroupItemElement",
  354. "resolved": "HTMLCalciteRadioGroupItemElement",
  355. "references": {
  356. "HTMLCalciteRadioGroupItemElement": {
  357. "location": "global"
  358. }
  359. }
  360. },
  361. "required": false,
  362. "optional": false,
  363. "docs": {
  364. "tags": [],
  365. "text": "The group's selected item."
  366. }
  367. },
  368. "width": {
  369. "type": "string",
  370. "mutable": false,
  371. "complexType": {
  372. "original": "Extract<\"auto\" | \"full\", Width>",
  373. "resolved": "\"auto\" | \"full\"",
  374. "references": {
  375. "Extract": {
  376. "location": "global"
  377. },
  378. "Width": {
  379. "location": "import",
  380. "path": "../interfaces"
  381. }
  382. }
  383. },
  384. "required": false,
  385. "optional": false,
  386. "docs": {
  387. "tags": [],
  388. "text": "specify the width of the group, defaults to auto"
  389. },
  390. "attribute": "width",
  391. "reflect": true,
  392. "defaultValue": "\"auto\""
  393. }
  394. }; }
  395. static get events() { return [{
  396. "method": "calciteRadioGroupChange",
  397. "name": "calciteRadioGroupChange",
  398. "bubbles": true,
  399. "cancelable": true,
  400. "composed": true,
  401. "docs": {
  402. "tags": [],
  403. "text": "Fired when the selected option changes, event detail is the new value"
  404. },
  405. "complexType": {
  406. "original": "string",
  407. "resolved": "string",
  408. "references": {}
  409. }
  410. }]; }
  411. static get methods() { return {
  412. "setFocus": {
  413. "complexType": {
  414. "signature": "() => Promise<void>",
  415. "parameters": [],
  416. "references": {
  417. "Promise": {
  418. "location": "global"
  419. }
  420. },
  421. "return": "Promise<void>"
  422. },
  423. "docs": {
  424. "text": "Sets focus on the component.",
  425. "tags": []
  426. }
  427. }
  428. }; }
  429. static get elementRef() { return "el"; }
  430. static get watchers() { return [{
  431. "propName": "value",
  432. "methodName": "valueHandler"
  433. }, {
  434. "propName": "selectedItem",
  435. "methodName": "handleSelectedItemChange"
  436. }]; }
  437. static get listeners() { return [{
  438. "name": "calciteRadioGroupItemChange",
  439. "method": "handleSelected",
  440. "target": undefined,
  441. "capture": false,
  442. "passive": false
  443. }, {
  444. "name": "keydown",
  445. "method": "handleKeyDown",
  446. "target": undefined,
  447. "capture": false,
  448. "passive": false
  449. }]; }
  450. }