card.js 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  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, Element, Event, h, Prop } from "@stencil/core";
  7. import { getSlotted, toAriaBoolean } from "../../utils/dom";
  8. import { CSS, SLOTS, TEXT } from "./resources";
  9. import { connectConditionalSlotComponent, disconnectConditionalSlotComponent } from "../../utils/conditionalSlot";
  10. /** Cards do not include a grid or bounding container
  11. * - cards will expand to fit the width of their container
  12. */
  13. /**
  14. * @slot - A slot for adding subheader/description content.
  15. * @slot thumbnail - A slot for adding a thumbnail to the card.
  16. * @slot title - A slot for adding a card title.
  17. * @slot subtitle - A slot for adding a card subtitle or short summary.
  18. * @slot footer-leading - A slot for adding a leading footer.
  19. * @slot footer-trailing - A slot for adding a trailing footer.
  20. */
  21. export class Card {
  22. constructor() {
  23. //--------------------------------------------------------------------------
  24. //
  25. // Public Properties
  26. //
  27. //--------------------------------------------------------------------------
  28. /** When true, the cards content is waiting to be loaded. This state shows a busy indicator.*/
  29. this.loading = false;
  30. /** Indicates whether the card is selected. */
  31. this.selected = false;
  32. /** Indicates whether the card is selectable. */
  33. this.selectable = false;
  34. /** string to override English loading text
  35. * @default "Loading"
  36. */
  37. this.intlLoading = TEXT.loading;
  38. /** string to override English select text for checkbox when selectable is true
  39. * @default "Select"
  40. */
  41. this.intlSelect = TEXT.select;
  42. /** string to override English deselect text for checkbox when selectable is true
  43. * @default "Deselect"
  44. */
  45. this.intlDeselect = TEXT.deselect;
  46. //--------------------------------------------------------------------------
  47. //
  48. // Private State/Props
  49. //
  50. //--------------------------------------------------------------------------
  51. //--------------------------------------------------------------------------
  52. //
  53. // Private Methods
  54. //
  55. //--------------------------------------------------------------------------
  56. this.cardSelectClick = () => {
  57. this.selectCard();
  58. };
  59. this.cardSelectKeyDown = (e) => {
  60. switch (e.key) {
  61. case " ":
  62. case "Enter":
  63. this.selectCard();
  64. e.preventDefault();
  65. break;
  66. }
  67. };
  68. }
  69. // --------------------------------------------------------------------------
  70. //
  71. // Lifecycle
  72. //
  73. // --------------------------------------------------------------------------
  74. connectedCallback() {
  75. connectConditionalSlotComponent(this);
  76. }
  77. disonnectedCallback() {
  78. disconnectConditionalSlotComponent(this);
  79. }
  80. render() {
  81. return (h("div", { class: "calcite-card-container" },
  82. this.loading ? (h("div", { class: "calcite-card-loader-container" },
  83. h("calcite-loader", { active: true, label: this.intlLoading }))) : null,
  84. h("section", { "aria-busy": toAriaBoolean(this.loading), class: { [CSS.container]: true } },
  85. this.selectable ? this.renderCheckbox() : null,
  86. this.renderThumbnail(),
  87. this.renderHeader(),
  88. h("div", { class: "card-content" },
  89. h("slot", null)),
  90. this.renderFooter())));
  91. }
  92. selectCard() {
  93. this.selected = !this.selected;
  94. this.calciteCardSelect.emit();
  95. }
  96. renderThumbnail() {
  97. return getSlotted(this.el, SLOTS.thumbnail) ? (h("div", { class: CSS.thumbnailWrapper, key: "thumbnail-wrapper" },
  98. h("slot", { name: SLOTS.thumbnail }))) : null;
  99. }
  100. renderCheckbox() {
  101. const checkboxLabel = this.selected ? this.intlDeselect : this.intlSelect;
  102. return (h("calcite-label", { class: CSS.checkboxWrapper, onClick: this.cardSelectClick, onKeyDown: this.cardSelectKeyDown },
  103. h("calcite-checkbox", { checked: this.selected, label: checkboxLabel })));
  104. }
  105. renderHeader() {
  106. const { el } = this;
  107. const title = getSlotted(el, SLOTS.title);
  108. const subtitle = getSlotted(el, SLOTS.subtitle);
  109. const hasHeader = title || subtitle;
  110. return hasHeader ? (h("header", { class: CSS.header },
  111. h("slot", { name: SLOTS.title }),
  112. h("slot", { name: SLOTS.subtitle }))) : null;
  113. }
  114. renderFooter() {
  115. const { el } = this;
  116. const leadingFooter = getSlotted(el, SLOTS.footerLeading);
  117. const trailingFooter = getSlotted(el, SLOTS.footerTrailing);
  118. const hasFooter = leadingFooter || trailingFooter;
  119. return hasFooter ? (h("footer", { class: CSS.footer },
  120. h("slot", { name: SLOTS.footerLeading }),
  121. h("slot", { name: SLOTS.footerTrailing }))) : null;
  122. }
  123. static get is() { return "calcite-card"; }
  124. static get encapsulation() { return "shadow"; }
  125. static get originalStyleUrls() { return {
  126. "$": ["card.scss"]
  127. }; }
  128. static get styleUrls() { return {
  129. "$": ["card.css"]
  130. }; }
  131. static get properties() { return {
  132. "loading": {
  133. "type": "boolean",
  134. "mutable": false,
  135. "complexType": {
  136. "original": "boolean",
  137. "resolved": "boolean",
  138. "references": {}
  139. },
  140. "required": false,
  141. "optional": false,
  142. "docs": {
  143. "tags": [],
  144. "text": "When true, the cards content is waiting to be loaded. This state shows a busy indicator."
  145. },
  146. "attribute": "loading",
  147. "reflect": true,
  148. "defaultValue": "false"
  149. },
  150. "selected": {
  151. "type": "boolean",
  152. "mutable": true,
  153. "complexType": {
  154. "original": "boolean",
  155. "resolved": "boolean",
  156. "references": {}
  157. },
  158. "required": false,
  159. "optional": false,
  160. "docs": {
  161. "tags": [],
  162. "text": "Indicates whether the card is selected."
  163. },
  164. "attribute": "selected",
  165. "reflect": true,
  166. "defaultValue": "false"
  167. },
  168. "selectable": {
  169. "type": "boolean",
  170. "mutable": false,
  171. "complexType": {
  172. "original": "boolean",
  173. "resolved": "boolean",
  174. "references": {}
  175. },
  176. "required": false,
  177. "optional": false,
  178. "docs": {
  179. "tags": [],
  180. "text": "Indicates whether the card is selectable."
  181. },
  182. "attribute": "selectable",
  183. "reflect": true,
  184. "defaultValue": "false"
  185. },
  186. "intlLoading": {
  187. "type": "string",
  188. "mutable": false,
  189. "complexType": {
  190. "original": "string",
  191. "resolved": "string",
  192. "references": {}
  193. },
  194. "required": false,
  195. "optional": true,
  196. "docs": {
  197. "tags": [{
  198. "name": "default",
  199. "text": "\"Loading\""
  200. }],
  201. "text": "string to override English loading text"
  202. },
  203. "attribute": "intl-loading",
  204. "reflect": false,
  205. "defaultValue": "TEXT.loading"
  206. },
  207. "intlSelect": {
  208. "type": "string",
  209. "mutable": false,
  210. "complexType": {
  211. "original": "string",
  212. "resolved": "string",
  213. "references": {}
  214. },
  215. "required": false,
  216. "optional": false,
  217. "docs": {
  218. "tags": [{
  219. "name": "default",
  220. "text": "\"Select\""
  221. }],
  222. "text": "string to override English select text for checkbox when selectable is true"
  223. },
  224. "attribute": "intl-select",
  225. "reflect": false,
  226. "defaultValue": "TEXT.select"
  227. },
  228. "intlDeselect": {
  229. "type": "string",
  230. "mutable": false,
  231. "complexType": {
  232. "original": "string",
  233. "resolved": "string",
  234. "references": {}
  235. },
  236. "required": false,
  237. "optional": false,
  238. "docs": {
  239. "tags": [{
  240. "name": "default",
  241. "text": "\"Deselect\""
  242. }],
  243. "text": "string to override English deselect text for checkbox when selectable is true"
  244. },
  245. "attribute": "intl-deselect",
  246. "reflect": false,
  247. "defaultValue": "TEXT.deselect"
  248. }
  249. }; }
  250. static get events() { return [{
  251. "method": "calciteCardSelect",
  252. "name": "calciteCardSelect",
  253. "bubbles": true,
  254. "cancelable": true,
  255. "composed": true,
  256. "docs": {
  257. "tags": [],
  258. "text": "Fired when a selectable card is selected"
  259. },
  260. "complexType": {
  261. "original": "any",
  262. "resolved": "any",
  263. "references": {}
  264. }
  265. }]; }
  266. static get elementRef() { return "el"; }
  267. }