calcite-accordion-item.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  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 { proxyCustomElement, HTMLElement, createEvent, h, Host } from '@stencil/core/internal/client/index.js';
  7. import { g as getElementProp, b as getSlotted, c as getElementDir, C as CSS_UTILITY, t as toAriaBoolean } from './dom.js';
  8. import { c as connectConditionalSlotComponent, d as disconnectConditionalSlotComponent } from './conditionalSlot.js';
  9. import { d as defineCustomElement$2 } from './icon.js';
  10. const SLOTS = {
  11. actionsStart: "actions-start",
  12. actionsEnd: "actions-end"
  13. };
  14. const CSS = {
  15. icon: "icon",
  16. header: "header",
  17. headerContent: "header-content",
  18. actionsStart: "actions-start",
  19. actionsEnd: "actions-end",
  20. headerText: "header-text",
  21. heading: "heading",
  22. description: "description",
  23. expandIcon: "expand-icon",
  24. content: "content",
  25. iconStart: "icon--start",
  26. iconEnd: "icon--end",
  27. headerContainer: "header-container"
  28. };
  29. const accordionItemCss = "@keyframes in{0%{opacity:0}100%{opacity:1}}@keyframes in-down{0%{opacity:0;transform:translate3D(0, -5px, 0)}100%{opacity:1;transform:translate3D(0, 0, 0)}}@keyframes in-up{0%{opacity:0;transform:translate3D(0, 5px, 0)}100%{opacity:1;transform:translate3D(0, 0, 0)}}@keyframes in-scale{0%{opacity:0;transform:scale3D(0.95, 0.95, 1)}100%{opacity:1;transform:scale3D(1, 1, 1)}}:root{--calcite-animation-timing:calc(150ms * var(--calcite-internal-duration-factor));--calcite-internal-duration-factor:var(--calcite-duration-factor, 1);--calcite-internal-animation-timing-fast:calc(100ms * var(--calcite-internal-duration-factor));--calcite-internal-animation-timing-medium:calc(200ms * var(--calcite-internal-duration-factor));--calcite-internal-animation-timing-slow:calc(300ms * var(--calcite-internal-duration-factor))}.calcite-animate{opacity:0;animation-fill-mode:both;animation-duration:var(--calcite-animation-timing)}.calcite-animate__in{animation-name:in}.calcite-animate__in-down{animation-name:in-down}.calcite-animate__in-up{animation-name:in-up}.calcite-animate__in-scale{animation-name:in-scale}@media (prefers-reduced-motion: reduce){:root{--calcite-internal-duration-factor:0.01}}:root{--calcite-floating-ui-transition:var(--calcite-animation-timing)}:host([hidden]){display:none}.icon-position--end,.icon-position--start{--calcite-accordion-item-icon-rotation:calc(90deg * -1);--calcite-accordion-item-active-icon-rotation:0deg;--calcite-accordion-item-icon-rotation-rtl:90deg;--calcite-accordion-item-active-icon-rotation-rtl:0deg}.icon-position--start{--calcite-accordion-item-flex-direction:row-reverse;--calcite-accordion-item-icon-spacing-start:0;--calcite-accordion-item-icon-spacing-end:var(--calcite-accordion-icon-margin)}.icon-position--end{--calcite-accordion-item-flex-direction:row;--calcite-accordion-item-icon-spacing-start:var(--calcite-accordion-icon-margin);--calcite-accordion-item-icon-spacing-end:0}.icon-position--end:not(.icon-type--plus-minus){--calcite-accordion-item-icon-rotation:0deg;--calcite-accordion-item-active-icon-rotation:180deg;--calcite-accordion-item-icon-rotation-rtl:0deg;--calcite-accordion-item-active-icon-rotation-rtl:calc(180deg * -1)}:host{position:relative;display:flex;flex-direction:column;color:var(--calcite-ui-text-3);text-decoration-line:none;background-color:var(--calcite-accordion-item-background, var(--calcite-ui-foreground-1))}:host .header-content{outline-color:transparent}:host(:focus) .header-content{outline:2px solid transparent;outline:2px solid var(--calcite-ui-brand);outline-offset:-2px}:host([expanded]){color:var(--calcite-ui-text-1)}:host([expanded]) .content{display:block;color:var(--calcite-ui-text-1)}:host([expanded]) .header{border-block-end-color:transparent}:host .header{display:flex;align-items:stretch}:host .icon{position:relative;margin:0px;display:inline-flex;color:var(--calcite-ui-text-3);transition-duration:150ms;transition-timing-function:cubic-bezier(0.4, 0, 0.2, 1);margin-inline-end:var(--calcite-accordion-item-icon-spacing-start);margin-inline-start:var(--calcite-accordion-item-icon-spacing-end)}.icon--start{display:flex;align-items:center;margin-inline-end:var(--calcite-accordion-icon-margin)}.icon--end{display:flex;align-items:center;margin-inline-end:var(--calcite-accordion-icon-margin);margin-inline-start:var(--calcite-accordion-icon-margin)}.header-container{inline-size:100%}.content{padding:var(--calcite-accordion-item-padding)}:host .content,:host .header{border-block-end:1px solid var(--calcite-accordion-item-border, var(--calcite-ui-border-2))}:host .header *{display:inline-flex;align-items:center;transition-duration:150ms;transition-timing-function:cubic-bezier(0.4, 0, 0.2, 1)}:host .content{display:none;padding-block-start:0px;color:var(--calcite-ui-text-3);text-align:initial}:host .expand-icon{color:var(--calcite-ui-text-3);margin-inline-start:var(--calcite-accordion-item-icon-spacing-start);margin-inline-end:var(--calcite-accordion-item-icon-spacing-end);transform:rotate(var(--calcite-accordion-item-icon-rotation))}.calcite--rtl .expand-icon{transform:rotate(var(--calcite-accordion-item-icon-rotation-rtl))}:host([expanded]) .expand-icon{color:var(--calcite-ui-text-1);transform:rotate(var(--calcite-accordion-item-active-icon-rotation))}:host([expanded]) .calcite--rtl .expand-icon{transform:rotate(var(--calcite-accordion-item-active-icon-rotation-rtl))}:host .header-text{margin-block:0px;flex-grow:1;flex-direction:column;padding-block:0px;text-align:initial;margin-inline-end:auto}:host .heading,:host .description{display:flex;inline-size:100%}:host .heading{font-weight:var(--calcite-font-weight-medium);color:var(--calcite-ui-text-2)}:host .description{margin-block-start:0.25rem;color:var(--calcite-ui-text-3)}:host(:focus) .heading,:host(:hover) .heading{color:var(--calcite-ui-text-1)}:host(:focus) .icon,:host(:hover) .icon{color:var(--calcite-ui-text-1)}:host(:focus) .expand-icon,:host(:hover) .expand-icon{color:var(--calcite-ui-text-1)}:host(:focus) .description,:host(:hover) .description{color:var(--calcite-ui-text-2)}:host(:focus) .heading,:host(:active) .heading,:host([expanded]) .heading{color:var(--calcite-ui-text-1)}:host(:focus) .icon,:host(:active) .icon,:host([expanded]) .icon{color:var(--calcite-ui-text-1)}:host(:focus) .expand-icon,:host(:active) .expand-icon,:host([expanded]) .expand-icon{color:var(--calcite-ui-text-1)}:host(:focus) .description,:host(:active) .description,:host([expanded]) .description{color:var(--calcite-ui-text-2)}.header-content{flex-grow:1;cursor:pointer;padding:var(--calcite-accordion-item-padding);flex-direction:var(--calcite-accordion-item-flex-direction)}.actions-start,.actions-end{display:flex;align-items:center}@media (forced-colors: active){:host([expanded]) .header{border-block-end:none}:host([expanded]) .heading{font-weight:bolder}:host(:hover) .heading,:host(:focus) .heading{text-decoration:underline}}";
  30. const AccordionItem = /*@__PURE__*/ proxyCustomElement(class extends HTMLElement {
  31. constructor() {
  32. super();
  33. this.__registerHost();
  34. this.__attachShadow();
  35. this.calciteInternalAccordionItemKeyEvent = createEvent(this, "calciteInternalAccordionItemKeyEvent", 6);
  36. this.calciteInternalAccordionItemSelect = createEvent(this, "calciteInternalAccordionItemSelect", 6);
  37. this.calciteInternalAccordionItemClose = createEvent(this, "calciteInternalAccordionItemClose", 6);
  38. this.calciteInternalAccordionItemRegister = createEvent(this, "calciteInternalAccordionItemRegister", 6);
  39. //--------------------------------------------------------------------------
  40. //
  41. // Public Properties
  42. //
  43. //--------------------------------------------------------------------------
  44. /**
  45. * When `true`, the component is active.
  46. *
  47. * @deprecated use `expanded` instead.
  48. */
  49. this.active = false;
  50. /** When `true`, the component is expanded. */
  51. this.expanded = false;
  52. /** what icon position does the parent accordion specify */
  53. this.iconPosition = "end";
  54. /** handle clicks on item header */
  55. this.itemHeaderClickHandler = () => this.emitRequestedItem();
  56. }
  57. activeHandler(value) {
  58. this.expanded = value;
  59. }
  60. expandedHandler(value) {
  61. this.active = value;
  62. }
  63. iconHandler(value) {
  64. this.iconStart = value;
  65. }
  66. iconStartHandler(value) {
  67. this.icon = value;
  68. }
  69. //--------------------------------------------------------------------------
  70. //
  71. // Lifecycle
  72. //
  73. //--------------------------------------------------------------------------
  74. connectedCallback() {
  75. this.parent = this.el.parentElement;
  76. this.selectionMode = getElementProp(this.el, "selection-mode", "multi");
  77. this.iconType = getElementProp(this.el, "icon-type", "chevron");
  78. this.iconPosition = getElementProp(this.el, "icon-position", this.iconPosition);
  79. const isExpanded = this.active || this.expanded;
  80. if (isExpanded) {
  81. this.activeHandler(isExpanded);
  82. this.expandedHandler(isExpanded);
  83. }
  84. if (this.iconStart) {
  85. this.icon = this.iconStart;
  86. }
  87. else if (this.icon) {
  88. this.iconStart = this.icon;
  89. }
  90. connectConditionalSlotComponent(this);
  91. }
  92. componentDidLoad() {
  93. this.itemPosition = this.getItemPosition();
  94. this.calciteInternalAccordionItemRegister.emit({
  95. parent: this.parent,
  96. position: this.itemPosition
  97. });
  98. }
  99. disconnectedCallback() {
  100. disconnectConditionalSlotComponent(this);
  101. }
  102. // --------------------------------------------------------------------------
  103. //
  104. // Render Methods
  105. //
  106. // --------------------------------------------------------------------------
  107. renderActionsStart() {
  108. const { el } = this;
  109. return getSlotted(el, SLOTS.actionsStart) ? (h("div", { class: CSS.actionsStart }, h("slot", { name: SLOTS.actionsStart }))) : null;
  110. }
  111. renderActionsEnd() {
  112. const { el } = this;
  113. return getSlotted(el, SLOTS.actionsEnd) ? (h("div", { class: CSS.actionsEnd }, h("slot", { name: SLOTS.actionsEnd }))) : null;
  114. }
  115. render() {
  116. const dir = getElementDir(this.el);
  117. const iconStartEl = this.iconStart ? (h("calcite-icon", { class: CSS.iconStart, icon: this.iconStart, key: "icon-start", scale: "s" })) : null;
  118. const iconEndEl = this.iconEnd ? (h("calcite-icon", { class: CSS.iconEnd, icon: this.iconEnd, key: "icon-end", scale: "s" })) : null;
  119. const description = this.description || this.itemSubtitle;
  120. return (h(Host, null, h("div", { class: {
  121. [`icon-position--${this.iconPosition}`]: true,
  122. [`icon-type--${this.iconType}`]: true
  123. } }, h("div", { class: { [CSS.header]: true, [CSS_UTILITY.rtl]: dir === "rtl" } }, this.renderActionsStart(), h("div", { "aria-expanded": toAriaBoolean(this.active || this.expanded), class: CSS.headerContent, onClick: this.itemHeaderClickHandler, role: "button", tabindex: "0" }, h("div", { class: CSS.headerContainer }, iconStartEl, h("div", { class: CSS.headerText }, h("span", { class: CSS.heading }, this.heading || this.itemTitle), description ? h("span", { class: CSS.description }, description) : null), iconEndEl), h("calcite-icon", { class: CSS.expandIcon, icon: this.iconType === "chevron"
  124. ? "chevronDown"
  125. : this.iconType === "caret"
  126. ? "caretDown"
  127. : this.expanded || this.active
  128. ? "minus"
  129. : "plus", scale: "s" })), this.renderActionsEnd()), h("div", { class: CSS.content }, h("slot", null)))));
  130. }
  131. //--------------------------------------------------------------------------
  132. //
  133. // Event Listeners
  134. //
  135. //--------------------------------------------------------------------------
  136. keyDownHandler(event) {
  137. if (event.target === this.el) {
  138. switch (event.key) {
  139. case " ":
  140. case "Enter":
  141. this.emitRequestedItem();
  142. event.preventDefault();
  143. break;
  144. case "ArrowUp":
  145. case "ArrowDown":
  146. case "Home":
  147. case "End":
  148. this.calciteInternalAccordionItemKeyEvent.emit({
  149. parent: this.parent,
  150. item: event
  151. });
  152. event.preventDefault();
  153. break;
  154. }
  155. }
  156. }
  157. updateActiveItemOnChange(event) {
  158. this.requestedAccordionItem = event.detail
  159. .requestedAccordionItem;
  160. if (this.el.parentNode !== this.requestedAccordionItem.parentNode) {
  161. return;
  162. }
  163. this.determineActiveItem();
  164. event.stopPropagation();
  165. }
  166. //--------------------------------------------------------------------------
  167. //
  168. // Private Methods
  169. //
  170. //--------------------------------------------------------------------------
  171. determineActiveItem() {
  172. switch (this.selectionMode) {
  173. case "multi":
  174. if (this.el === this.requestedAccordionItem) {
  175. this.expanded = !this.expanded;
  176. }
  177. break;
  178. case "single":
  179. this.expanded = this.el === this.requestedAccordionItem ? !this.expanded : false;
  180. break;
  181. case "single-persist":
  182. this.expanded = this.el === this.requestedAccordionItem;
  183. break;
  184. }
  185. }
  186. emitRequestedItem() {
  187. this.calciteInternalAccordionItemSelect.emit({
  188. requestedAccordionItem: this.el
  189. });
  190. }
  191. getItemPosition() {
  192. return Array.prototype.indexOf.call(this.parent.querySelectorAll("calcite-accordion-item"), this.el);
  193. }
  194. get el() { return this; }
  195. static get watchers() { return {
  196. "active": ["activeHandler"],
  197. "expanded": ["expandedHandler"],
  198. "icon": ["iconHandler"],
  199. "iconStart": ["iconStartHandler"]
  200. }; }
  201. static get style() { return accordionItemCss; }
  202. }, [1, "calcite-accordion-item", {
  203. "active": [1540],
  204. "expanded": [1540],
  205. "itemTitle": [1, "item-title"],
  206. "itemSubtitle": [1, "item-subtitle"],
  207. "heading": [1],
  208. "description": [1],
  209. "icon": [1537],
  210. "iconStart": [513, "icon-start"],
  211. "iconEnd": [513, "icon-end"]
  212. }, [[0, "keydown", "keyDownHandler"], [16, "calciteInternalAccordionChange", "updateActiveItemOnChange"]]]);
  213. function defineCustomElement$1() {
  214. if (typeof customElements === "undefined") {
  215. return;
  216. }
  217. const components = ["calcite-accordion-item", "calcite-icon"];
  218. components.forEach(tagName => { switch (tagName) {
  219. case "calcite-accordion-item":
  220. if (!customElements.get(tagName)) {
  221. customElements.define(tagName, AccordionItem);
  222. }
  223. break;
  224. case "calcite-icon":
  225. if (!customElements.get(tagName)) {
  226. defineCustomElement$2();
  227. }
  228. break;
  229. } });
  230. }
  231. defineCustomElement$1();
  232. const CalciteAccordionItem = AccordionItem;
  233. const defineCustomElement = defineCustomElement$1;
  234. export { CalciteAccordionItem, defineCustomElement };