chunk.2YBFIIL4.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. import {
  2. focusVisible
  3. } from "./chunk.XAZSQ3AT.js";
  4. import {
  5. getOffset,
  6. scrollIntoView
  7. } from "./chunk.XAZN5AQ5.js";
  8. import {
  9. event,
  10. watch
  11. } from "./chunk.XX234VRK.js";
  12. import {
  13. e as e2
  14. } from "./chunk.YXKHB4AC.js";
  15. import {
  16. T,
  17. e,
  18. h,
  19. n,
  20. o,
  21. r,
  22. r2
  23. } from "./chunk.5PIDMFOE.js";
  24. import {
  25. __decorateClass
  26. } from "./chunk.IHGPZX35.js";
  27. // _uyihdawu1:/Users/claviska/Projects/shoelace/src/components/tab-group/tab-group.scss
  28. var tab_group_default = ":host {\n position: relative;\n box-sizing: border-box;\n}\n:host *, :host *:before, :host *:after {\n box-sizing: inherit;\n}\n\n[hidden] {\n display: none !important;\n}\n\n:host {\n --tabs-border-color: var(--sl-color-gray-200);\n display: block;\n}\n\n.tab-group {\n display: flex;\n border: solid 1px transparent;\n border-radius: 0;\n}\n.tab-group .tab-group__tabs {\n display: flex;\n position: relative;\n}\n.tab-group .tab-group__indicator {\n position: absolute;\n left: 0;\n transition: var(--sl-transition-fast) transform ease, var(--sl-transition-fast) width ease;\n}\n.tab-group:not(.focus-visible) ::slotted(sl-tab) {\n --focus-ring: none;\n}\n\n.tab-group--has-scroll-controls .tab-group__nav-container {\n position: relative;\n padding: 0 var(--sl-spacing-x-large);\n}\n\n.tab-group__scroll-button {\n display: flex;\n align-items: center;\n justify-content: center;\n position: absolute;\n top: 0;\n bottom: 0;\n width: var(--sl-spacing-x-large);\n}\n\n.tab-group__scroll-button--start {\n left: 0;\n}\n\n.tab-group__scroll-button--end {\n right: 0;\n}\n\n.tab-group--top {\n flex-direction: column;\n}\n.tab-group--top .tab-group__nav-container {\n order: 1;\n}\n.tab-group--top .tab-group__nav {\n display: flex;\n overflow-x: auto;\n scrollbar-width: none;\n -ms-overflow-style: none;\n}\n.tab-group--top .tab-group__nav::-webkit-scrollbar {\n width: 0;\n height: 0;\n}\n.tab-group--top .tab-group__tabs {\n flex: 1 1 auto;\n position: relative;\n flex-direction: row;\n border-bottom: solid 2px var(--tabs-border-color);\n}\n.tab-group--top .tab-group__indicator {\n bottom: -2px;\n border-bottom: solid 2px var(--sl-color-primary-500);\n}\n.tab-group--top .tab-group__body {\n order: 2;\n}\n\n.tab-group--bottom {\n flex-direction: column;\n}\n.tab-group--bottom .tab-group__nav-container {\n order: 2;\n}\n.tab-group--bottom .tab-group__nav {\n display: flex;\n overflow-x: auto;\n scrollbar-width: none;\n -ms-overflow-style: none;\n}\n.tab-group--bottom .tab-group__nav::-webkit-scrollbar {\n width: 0;\n height: 0;\n}\n.tab-group--bottom .tab-group__tabs {\n flex: 1 1 auto;\n position: relative;\n flex-direction: row;\n border-top: solid 2px var(--tabs-border-color);\n}\n.tab-group--bottom .tab-group__indicator {\n top: calc(-1 * 2px);\n border-top: solid 2px var(--sl-color-primary-500);\n}\n.tab-group--bottom .tab-group__body {\n order: 1;\n}\n\n.tab-group--start {\n flex-direction: row;\n}\n.tab-group--start .tab-group__nav-container {\n order: 1;\n}\n.tab-group--start .tab-group__tabs {\n flex: 0 0 auto;\n flex-direction: column;\n border-right: solid 2px var(--tabs-border-color);\n}\n.tab-group--start .tab-group__indicator {\n right: calc(-1 * 2px);\n border-right: solid 2px var(--sl-color-primary-500);\n}\n.tab-group--start .tab-group__body {\n flex: 1 1 auto;\n order: 2;\n}\n\n.tab-group--end {\n flex-direction: row;\n}\n.tab-group--end .tab-group__nav-container {\n order: 2;\n}\n.tab-group--end .tab-group__tabs {\n flex: 0 0 auto;\n flex-direction: column;\n border-left: solid 2px var(--tabs-border-color);\n}\n.tab-group--end .tab-group__indicator {\n left: calc(-1 * 2px);\n border-left: solid 2px var(--sl-color-primary-500);\n}\n.tab-group--end .tab-group__body {\n flex: 1 1 auto;\n order: 1;\n}";
  29. // src/components/tab-group/tab-group.ts
  30. var SlTabGroup = class extends h {
  31. constructor() {
  32. super(...arguments);
  33. this.tabs = [];
  34. this.panels = [];
  35. this.hasScrollControls = false;
  36. this.placement = "top";
  37. this.activation = "auto";
  38. this.noScrollControls = false;
  39. }
  40. connectedCallback() {
  41. super.connectedCallback();
  42. this.resizeObserver = new ResizeObserver(() => {
  43. this.preventIndicatorTransition();
  44. this.repositionIndicator();
  45. this.updateScrollControls();
  46. });
  47. this.mutationObserver = new MutationObserver((mutations) => {
  48. if (mutations.some((m) => !["aria-labelledby", "aria-controls"].includes(m.attributeName))) {
  49. setTimeout(() => this.setAriaLabels());
  50. }
  51. if (mutations.some((m) => m.attributeName === "disabled")) {
  52. this.syncTabsAndPanels();
  53. }
  54. });
  55. this.updateComplete.then(() => {
  56. this.syncTabsAndPanels();
  57. this.mutationObserver.observe(this, { attributes: true, childList: true, subtree: true });
  58. this.resizeObserver.observe(this.nav);
  59. focusVisible.observe(this.tabGroup);
  60. const intersectionObserver = new IntersectionObserver((entries, observer) => {
  61. if (entries[0].intersectionRatio > 0) {
  62. this.setAriaLabels();
  63. this.setActiveTab(this.getActiveTab() || this.tabs[0], { emitEvents: false });
  64. observer.unobserve(entries[0].target);
  65. }
  66. });
  67. intersectionObserver.observe(this.tabGroup);
  68. });
  69. }
  70. disconnectedCallback() {
  71. this.mutationObserver.disconnect();
  72. this.resizeObserver.unobserve(this.nav);
  73. focusVisible.unobserve(this.tabGroup);
  74. }
  75. show(panel) {
  76. const tab = this.tabs.find((el) => el.panel === panel);
  77. if (tab) {
  78. this.setActiveTab(tab, { scrollBehavior: "smooth" });
  79. }
  80. }
  81. getAllTabs(includeDisabled = false) {
  82. const slot = this.shadowRoot.querySelector('slot[name="nav"]');
  83. return [...slot.assignedElements()].filter((el) => {
  84. return includeDisabled ? el.tagName.toLowerCase() === "sl-tab" : el.tagName.toLowerCase() === "sl-tab" && !el.disabled;
  85. });
  86. }
  87. getAllPanels() {
  88. const slot = this.body.querySelector("slot");
  89. return [...slot.assignedElements()].filter((el) => el.tagName.toLowerCase() === "sl-tab-panel");
  90. }
  91. getActiveTab() {
  92. return this.tabs.find((el) => el.active);
  93. }
  94. handleClick(event2) {
  95. const target = event2.target;
  96. const tab = target.closest("sl-tab");
  97. const tabGroup = tab == null ? void 0 : tab.closest("sl-tab-group");
  98. if (tabGroup !== this) {
  99. return;
  100. }
  101. if (tab) {
  102. this.setActiveTab(tab, { scrollBehavior: "smooth" });
  103. }
  104. }
  105. handleKeyDown(event2) {
  106. const target = event2.target;
  107. const tab = target.closest("sl-tab");
  108. const tabGroup = tab == null ? void 0 : tab.closest("sl-tab-group");
  109. if (tabGroup !== this) {
  110. return;
  111. }
  112. if (["Enter", " "].includes(event2.key)) {
  113. if (tab) {
  114. this.setActiveTab(tab, { scrollBehavior: "smooth" });
  115. event2.preventDefault();
  116. }
  117. }
  118. if (["ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown", "Home", "End"].includes(event2.key)) {
  119. const activeEl = document.activeElement;
  120. if (activeEl && activeEl.tagName.toLowerCase() === "sl-tab") {
  121. let index = this.tabs.indexOf(activeEl);
  122. if (event2.key === "Home") {
  123. index = 0;
  124. } else if (event2.key === "End") {
  125. index = this.tabs.length - 1;
  126. } else if (event2.key === "ArrowLeft") {
  127. index = Math.max(0, index - 1);
  128. } else if (event2.key === "ArrowRight") {
  129. index = Math.min(this.tabs.length - 1, index + 1);
  130. }
  131. this.tabs[index].focus({ preventScroll: true });
  132. if (this.activation === "auto") {
  133. this.setActiveTab(this.tabs[index], { scrollBehavior: "smooth" });
  134. }
  135. if (["top", "bottom"].includes(this.placement)) {
  136. scrollIntoView(this.tabs[index], this.nav, "horizontal");
  137. }
  138. event2.preventDefault();
  139. }
  140. }
  141. }
  142. handleScrollToStart() {
  143. this.nav.scroll({
  144. left: this.nav.scrollLeft - this.nav.clientWidth,
  145. behavior: "smooth"
  146. });
  147. }
  148. handleScrollToEnd() {
  149. this.nav.scroll({
  150. left: this.nav.scrollLeft + this.nav.clientWidth,
  151. behavior: "smooth"
  152. });
  153. }
  154. updateScrollControls() {
  155. if (this.noScrollControls) {
  156. this.hasScrollControls = false;
  157. } else {
  158. this.hasScrollControls = ["top", "bottom"].includes(this.placement) && this.nav.scrollWidth > this.nav.clientWidth;
  159. }
  160. }
  161. setActiveTab(tab, options) {
  162. options = Object.assign({
  163. emitEvents: true,
  164. scrollBehavior: "auto"
  165. }, options);
  166. if (tab && tab !== this.activeTab && !tab.disabled) {
  167. const previousTab = this.activeTab;
  168. this.activeTab = tab;
  169. this.tabs.map((el) => el.active = el === this.activeTab);
  170. this.panels.map((el) => el.active = el.name === this.activeTab.panel);
  171. this.syncIndicator();
  172. if (["top", "bottom"].includes(this.placement)) {
  173. scrollIntoView(this.activeTab, this.nav, "horizontal", options.scrollBehavior);
  174. }
  175. if (options.emitEvents) {
  176. if (previousTab) {
  177. this.slTabHide.emit({ detail: { name: previousTab.panel } });
  178. }
  179. this.slTabShow.emit({ detail: { name: this.activeTab.panel } });
  180. }
  181. }
  182. }
  183. setAriaLabels() {
  184. this.tabs.map((tab) => {
  185. const panel = this.panels.find((el) => el.name === tab.panel);
  186. if (panel) {
  187. tab.setAttribute("aria-controls", panel.getAttribute("id"));
  188. panel.setAttribute("aria-labelledby", tab.getAttribute("id"));
  189. }
  190. });
  191. }
  192. syncIndicator() {
  193. if (this.indicator) {
  194. const tab = this.getActiveTab();
  195. if (tab) {
  196. this.indicator.style.display = "block";
  197. this.repositionIndicator();
  198. } else {
  199. this.indicator.style.display = "none";
  200. return;
  201. }
  202. }
  203. }
  204. repositionIndicator() {
  205. const currentTab = this.getActiveTab();
  206. if (!currentTab) {
  207. return;
  208. }
  209. const width = currentTab.clientWidth;
  210. const height = currentTab.clientHeight;
  211. const offset = getOffset(currentTab, this.nav);
  212. const offsetTop = offset.top + this.nav.scrollTop;
  213. const offsetLeft = offset.left + this.nav.scrollLeft;
  214. switch (this.placement) {
  215. case "top":
  216. case "bottom":
  217. this.indicator.style.width = `${width}px`;
  218. this.indicator.style.height = "auto";
  219. this.indicator.style.transform = `translateX(${offsetLeft}px)`;
  220. break;
  221. case "start":
  222. case "end":
  223. this.indicator.style.width = "auto";
  224. this.indicator.style.height = `${height}px`;
  225. this.indicator.style.transform = `translateY(${offsetTop}px)`;
  226. break;
  227. }
  228. }
  229. preventIndicatorTransition() {
  230. const transitionValue = this.indicator.style.transition;
  231. this.indicator.style.transition = "none";
  232. requestAnimationFrame(() => {
  233. this.indicator.style.transition = transitionValue;
  234. });
  235. }
  236. syncTabsAndPanels() {
  237. this.tabs = this.getAllTabs();
  238. this.panels = this.getAllPanels();
  239. this.syncIndicator();
  240. }
  241. render() {
  242. return T`
  243. <div
  244. part="base"
  245. class=${e2({
  246. "tab-group": true,
  247. "tab-group--top": this.placement === "top",
  248. "tab-group--bottom": this.placement === "bottom",
  249. "tab-group--start": this.placement === "start",
  250. "tab-group--end": this.placement === "end",
  251. "tab-group--has-scroll-controls": this.hasScrollControls
  252. })}
  253. @click=${this.handleClick}
  254. @keydown=${this.handleKeyDown}
  255. >
  256. <div class="tab-group__nav-container">
  257. ${this.hasScrollControls ? T`
  258. <sl-icon-button
  259. class="tab-group__scroll-button tab-group__scroll-button--start"
  260. exportparts="base:scroll-button"
  261. name="chevron-left"
  262. library="system"
  263. @click=${this.handleScrollToStart}
  264. ></sl-icon-button>
  265. ` : ""}
  266. <div part="nav" class="tab-group__nav">
  267. <div part="tabs" class="tab-group__tabs" role="tablist">
  268. <div part="active-tab-indicator" class="tab-group__indicator"></div>
  269. <slot name="nav" @slotchange=${this.syncTabsAndPanels}></slot>
  270. </div>
  271. </div>
  272. ${this.hasScrollControls ? T`
  273. <sl-icon-button
  274. class="tab-group__scroll-button tab-group__scroll-button--end"
  275. exportparts="base:scroll-button"
  276. name="chevron-right"
  277. library="system"
  278. @click=${this.handleScrollToEnd}
  279. ></sl-icon-button>
  280. ` : ""}
  281. </div>
  282. <div part="body" class="tab-group__body">
  283. <slot @slotchange=${this.syncTabsAndPanels}></slot>
  284. </div>
  285. </div>
  286. `;
  287. }
  288. };
  289. SlTabGroup.styles = r(tab_group_default);
  290. __decorateClass([
  291. o(".tab-group")
  292. ], SlTabGroup.prototype, "tabGroup", 2);
  293. __decorateClass([
  294. o(".tab-group__body")
  295. ], SlTabGroup.prototype, "body", 2);
  296. __decorateClass([
  297. o(".tab-group__nav")
  298. ], SlTabGroup.prototype, "nav", 2);
  299. __decorateClass([
  300. o(".tab-group__indicator")
  301. ], SlTabGroup.prototype, "indicator", 2);
  302. __decorateClass([
  303. r2()
  304. ], SlTabGroup.prototype, "hasScrollControls", 2);
  305. __decorateClass([
  306. e()
  307. ], SlTabGroup.prototype, "placement", 2);
  308. __decorateClass([
  309. e()
  310. ], SlTabGroup.prototype, "activation", 2);
  311. __decorateClass([
  312. e({ attribute: "no-scroll-controls", type: Boolean })
  313. ], SlTabGroup.prototype, "noScrollControls", 2);
  314. __decorateClass([
  315. event("sl-tab-show")
  316. ], SlTabGroup.prototype, "slTabShow", 2);
  317. __decorateClass([
  318. event("sl-tab-hide")
  319. ], SlTabGroup.prototype, "slTabHide", 2);
  320. __decorateClass([
  321. watch("noScrollControls")
  322. ], SlTabGroup.prototype, "updateScrollControls", 1);
  323. __decorateClass([
  324. watch("placement")
  325. ], SlTabGroup.prototype, "syncIndicator", 1);
  326. SlTabGroup = __decorateClass([
  327. n("sl-tab-group")
  328. ], SlTabGroup);
  329. var tab_group_default2 = SlTabGroup;
  330. export {
  331. tab_group_default2 as tab_group_default
  332. };