action-bar.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  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, Host, Prop, Watch, h, Method } from "@stencil/core";
  7. import { ExpandToggle, toggleChildActionText } from "../functional/ExpandToggle";
  8. import { CSS, SLOTS, TEXT } from "./resources";
  9. import { getSlotted, focusElement } from "../../utils/dom";
  10. import { getOverflowCount, overflowActions, queryActions, overflowActionsDebounceInMs } from "./utils";
  11. import { createObserver } from "../../utils/observers";
  12. import { debounce } from "lodash-es";
  13. import { connectConditionalSlotComponent, disconnectConditionalSlotComponent } from "../../utils/conditionalSlot";
  14. /**
  15. * @slot - A slot for adding `calcite-action`s that will appear at the top of the action bar.
  16. * @slot bottom-actions - A slot for adding `calcite-action`s that will appear at the bottom of the action bar, above the collapse/expand button.
  17. * @slot expand-tooltip - Used to set the tooltip for the expand toggle.
  18. */
  19. export class ActionBar {
  20. constructor() {
  21. // --------------------------------------------------------------------------
  22. //
  23. // Properties
  24. //
  25. // --------------------------------------------------------------------------
  26. /**
  27. * When set to true, the expand-toggling behavior will be disabled.
  28. */
  29. this.expandDisabled = false;
  30. /**
  31. * Indicates whether widget is expanded.
  32. */
  33. this.expanded = false;
  34. /**
  35. * Disables automatically overflowing actions that won't fit into menus.
  36. */
  37. this.overflowActionsDisabled = false;
  38. this.mutationObserver = createObserver("mutation", () => {
  39. const { el, expanded } = this;
  40. toggleChildActionText({ parent: el, expanded });
  41. this.conditionallyOverflowActions();
  42. });
  43. this.resizeObserver = createObserver("resize", (entries) => this.resizeHandlerEntries(entries));
  44. // --------------------------------------------------------------------------
  45. //
  46. // Private Methods
  47. //
  48. // --------------------------------------------------------------------------
  49. this.actionMenuOpenChangeHandler = (event) => {
  50. if (event.detail) {
  51. const composedPath = event.composedPath();
  52. Array.from(this.el.querySelectorAll("calcite-action-group")).forEach((group) => {
  53. if (!composedPath.includes(group)) {
  54. group.menuOpen = false;
  55. }
  56. });
  57. }
  58. };
  59. this.resizeHandlerEntries = (entries) => {
  60. entries.forEach(this.resizeHandler);
  61. };
  62. this.resizeHandler = (entry) => {
  63. const { height } = entry.contentRect;
  64. this.resize(height);
  65. };
  66. this.resize = debounce((height) => {
  67. var _a;
  68. const { el, expanded, expandDisabled } = this;
  69. if (!height) {
  70. return;
  71. }
  72. const actions = queryActions(el);
  73. const actionCount = expandDisabled ? actions.length : actions.length + 1;
  74. const actionGroups = Array.from(el.querySelectorAll("calcite-action-group"));
  75. const groupCount = getSlotted(el, SLOTS.bottomActions) || !expandDisabled
  76. ? actionGroups.length + 1
  77. : actionGroups.length;
  78. const overflowCount = getOverflowCount({
  79. actionCount,
  80. actionHeight: (_a = actions[0]) === null || _a === void 0 ? void 0 : _a.clientHeight,
  81. height,
  82. groupCount
  83. });
  84. overflowActions({
  85. actionGroups,
  86. expanded,
  87. overflowCount
  88. });
  89. }, overflowActionsDebounceInMs);
  90. this.conditionallyOverflowActions = () => {
  91. if (!this.overflowActionsDisabled) {
  92. this.overflowActions();
  93. }
  94. };
  95. this.toggleExpand = () => {
  96. this.expanded = !this.expanded;
  97. this.calciteActionBarToggle.emit();
  98. };
  99. this.setExpandToggleRef = (el) => {
  100. this.expandToggleEl = el;
  101. };
  102. }
  103. expandHandler() {
  104. this.conditionallyOverflowActions();
  105. }
  106. expandedHandler(expanded) {
  107. toggleChildActionText({ parent: this.el, expanded });
  108. }
  109. overflowDisabledHandler(overflowActionsDisabled) {
  110. overflowActionsDisabled
  111. ? this.resizeObserver.disconnect()
  112. : this.resizeObserver.observe(this.el);
  113. }
  114. // --------------------------------------------------------------------------
  115. //
  116. // Lifecycle
  117. //
  118. // --------------------------------------------------------------------------
  119. componentDidLoad() {
  120. this.conditionallyOverflowActions();
  121. }
  122. connectedCallback() {
  123. var _a, _b;
  124. const { el, expanded } = this;
  125. toggleChildActionText({ parent: el, expanded });
  126. (_a = this.mutationObserver) === null || _a === void 0 ? void 0 : _a.observe(el, { childList: true, subtree: true });
  127. if (!this.overflowActionsDisabled) {
  128. (_b = this.resizeObserver) === null || _b === void 0 ? void 0 : _b.observe(el);
  129. }
  130. this.conditionallyOverflowActions();
  131. connectConditionalSlotComponent(this);
  132. }
  133. disconnectedCallback() {
  134. var _a, _b;
  135. (_a = this.mutationObserver) === null || _a === void 0 ? void 0 : _a.disconnect();
  136. (_b = this.resizeObserver) === null || _b === void 0 ? void 0 : _b.disconnect();
  137. disconnectConditionalSlotComponent(this);
  138. }
  139. // --------------------------------------------------------------------------
  140. //
  141. // Methods
  142. //
  143. // --------------------------------------------------------------------------
  144. /**
  145. * Overflows actions that won't fit into menus.
  146. * @internal
  147. */
  148. async overflowActions() {
  149. this.resize(this.el.clientHeight);
  150. }
  151. /** Sets focus on the component. */
  152. async setFocus(focusId) {
  153. if (focusId === "expand-toggle") {
  154. await focusElement(this.expandToggleEl);
  155. return;
  156. }
  157. this.el.focus();
  158. }
  159. // --------------------------------------------------------------------------
  160. //
  161. // Render Methods
  162. //
  163. // --------------------------------------------------------------------------
  164. renderBottomActionGroup() {
  165. const { expanded, expandDisabled, intlExpand, intlCollapse, el, position, toggleExpand, scale } = this;
  166. const tooltip = getSlotted(el, SLOTS.expandTooltip);
  167. const expandLabel = intlExpand || TEXT.expand;
  168. const collapseLabel = intlCollapse || TEXT.collapse;
  169. const expandToggleNode = !expandDisabled ? (h(ExpandToggle, { el: el, expanded: expanded, intlCollapse: collapseLabel, intlExpand: expandLabel, position: position, ref: this.setExpandToggleRef, scale: scale, toggle: toggleExpand, tooltip: tooltip })) : null;
  170. return getSlotted(el, SLOTS.bottomActions) || expandToggleNode ? (h("calcite-action-group", { class: CSS.actionGroupBottom, scale: scale },
  171. h("slot", { name: SLOTS.bottomActions }),
  172. h("slot", { name: SLOTS.expandTooltip }),
  173. expandToggleNode)) : null;
  174. }
  175. render() {
  176. return (h(Host, { onCalciteActionMenuOpenChange: this.actionMenuOpenChangeHandler },
  177. h("slot", null),
  178. this.renderBottomActionGroup()));
  179. }
  180. static get is() { return "calcite-action-bar"; }
  181. static get encapsulation() { return "shadow"; }
  182. static get originalStyleUrls() { return {
  183. "$": ["action-bar.scss"]
  184. }; }
  185. static get styleUrls() { return {
  186. "$": ["action-bar.css"]
  187. }; }
  188. static get properties() { return {
  189. "expandDisabled": {
  190. "type": "boolean",
  191. "mutable": false,
  192. "complexType": {
  193. "original": "boolean",
  194. "resolved": "boolean",
  195. "references": {}
  196. },
  197. "required": false,
  198. "optional": false,
  199. "docs": {
  200. "tags": [],
  201. "text": "When set to true, the expand-toggling behavior will be disabled."
  202. },
  203. "attribute": "expand-disabled",
  204. "reflect": true,
  205. "defaultValue": "false"
  206. },
  207. "expanded": {
  208. "type": "boolean",
  209. "mutable": true,
  210. "complexType": {
  211. "original": "boolean",
  212. "resolved": "boolean",
  213. "references": {}
  214. },
  215. "required": false,
  216. "optional": false,
  217. "docs": {
  218. "tags": [],
  219. "text": "Indicates whether widget is expanded."
  220. },
  221. "attribute": "expanded",
  222. "reflect": true,
  223. "defaultValue": "false"
  224. },
  225. "intlExpand": {
  226. "type": "string",
  227. "mutable": false,
  228. "complexType": {
  229. "original": "string",
  230. "resolved": "string",
  231. "references": {}
  232. },
  233. "required": false,
  234. "optional": true,
  235. "docs": {
  236. "tags": [],
  237. "text": "Updates the label of the expand icon when the component is not expanded."
  238. },
  239. "attribute": "intl-expand",
  240. "reflect": false
  241. },
  242. "intlCollapse": {
  243. "type": "string",
  244. "mutable": false,
  245. "complexType": {
  246. "original": "string",
  247. "resolved": "string",
  248. "references": {}
  249. },
  250. "required": false,
  251. "optional": true,
  252. "docs": {
  253. "tags": [],
  254. "text": "Updates the label of the collapse icon when the component is expanded."
  255. },
  256. "attribute": "intl-collapse",
  257. "reflect": false
  258. },
  259. "overflowActionsDisabled": {
  260. "type": "boolean",
  261. "mutable": false,
  262. "complexType": {
  263. "original": "boolean",
  264. "resolved": "boolean",
  265. "references": {}
  266. },
  267. "required": false,
  268. "optional": false,
  269. "docs": {
  270. "tags": [],
  271. "text": "Disables automatically overflowing actions that won't fit into menus."
  272. },
  273. "attribute": "overflow-actions-disabled",
  274. "reflect": false,
  275. "defaultValue": "false"
  276. },
  277. "position": {
  278. "type": "string",
  279. "mutable": false,
  280. "complexType": {
  281. "original": "Position",
  282. "resolved": "\"end\" | \"start\"",
  283. "references": {
  284. "Position": {
  285. "location": "import",
  286. "path": "../interfaces"
  287. }
  288. }
  289. },
  290. "required": false,
  291. "optional": false,
  292. "docs": {
  293. "tags": [],
  294. "text": "Arranges the component depending on the elements 'dir' property."
  295. },
  296. "attribute": "position",
  297. "reflect": true
  298. },
  299. "scale": {
  300. "type": "string",
  301. "mutable": false,
  302. "complexType": {
  303. "original": "Scale",
  304. "resolved": "\"l\" | \"m\" | \"s\"",
  305. "references": {
  306. "Scale": {
  307. "location": "import",
  308. "path": "../interfaces"
  309. }
  310. }
  311. },
  312. "required": false,
  313. "optional": false,
  314. "docs": {
  315. "tags": [],
  316. "text": "Specifies the size of the expand action."
  317. },
  318. "attribute": "scale",
  319. "reflect": true
  320. }
  321. }; }
  322. static get events() { return [{
  323. "method": "calciteActionBarToggle",
  324. "name": "calciteActionBarToggle",
  325. "bubbles": true,
  326. "cancelable": true,
  327. "composed": true,
  328. "docs": {
  329. "tags": [],
  330. "text": "Emitted when expanded has been toggled."
  331. },
  332. "complexType": {
  333. "original": "any",
  334. "resolved": "any",
  335. "references": {}
  336. }
  337. }]; }
  338. static get methods() { return {
  339. "overflowActions": {
  340. "complexType": {
  341. "signature": "() => Promise<void>",
  342. "parameters": [],
  343. "references": {
  344. "Promise": {
  345. "location": "global"
  346. }
  347. },
  348. "return": "Promise<void>"
  349. },
  350. "docs": {
  351. "text": "Overflows actions that won't fit into menus.",
  352. "tags": [{
  353. "name": "internal",
  354. "text": undefined
  355. }]
  356. }
  357. },
  358. "setFocus": {
  359. "complexType": {
  360. "signature": "(focusId?: \"expand-toggle\") => Promise<void>",
  361. "parameters": [{
  362. "tags": [],
  363. "text": ""
  364. }],
  365. "references": {
  366. "Promise": {
  367. "location": "global"
  368. }
  369. },
  370. "return": "Promise<void>"
  371. },
  372. "docs": {
  373. "text": "Sets focus on the component.",
  374. "tags": []
  375. }
  376. }
  377. }; }
  378. static get elementRef() { return "el"; }
  379. static get watchers() { return [{
  380. "propName": "expandDisabled",
  381. "methodName": "expandHandler"
  382. }, {
  383. "propName": "expanded",
  384. "methodName": "expandedHandler"
  385. }, {
  386. "propName": "overflowActionsDisabled",
  387. "methodName": "overflowDisabledHandler"
  388. }]; }
  389. }