@abydahana/aksara-ui 1.2.3 → 1.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/aksara.css CHANGED
@@ -23496,6 +23496,18 @@ img,svg,video {
23496
23496
  border-radius:0;
23497
23497
  overflow:visible
23498
23498
  }
23499
+ .list-group-inset {
23500
+ border-radius:var(--aksara-radius-lg,1rem);
23501
+ background:var(--aksara-bg-subtle);
23502
+ border-color:var(--aksara-border-subtle);
23503
+ overflow:hidden
23504
+ }
23505
+ .list-group-inset .list-group-item {
23506
+ background:transparent
23507
+ }
23508
+ .list-group-inset .list-group-item-action:hover,.list-group-inset .list-group-item-action:focus {
23509
+ background:rgb(var(--aksara-primary)/.06)
23510
+ }
23499
23511
  .list-group-item {
23500
23512
  display:block;
23501
23513
  padding:1rem;
@@ -24271,6 +24283,28 @@ img,svg,video {
24271
24283
  .tab-panel {
24272
24284
  padding:1rem 0
24273
24285
  }
24286
+ .tabs-slider {
24287
+ position:relative;
24288
+ overflow:hidden;
24289
+ width:100%
24290
+ }
24291
+ .tabs-slider-track {
24292
+ display:flex;
24293
+ flex-direction:row;
24294
+ flex-wrap:nowrap;
24295
+ width:100%;
24296
+ will-change:transform;
24297
+ touch-action:pan-y pinch-zoom;
24298
+ transition:transform .3s cubic-bezier(.25,1,.5,1)
24299
+ }
24300
+ .tabs-slider-track > .tab-panel,.tabs-slider-track > .tab-pane {
24301
+ flex:0 0 100%;
24302
+ min-width:100%;
24303
+ max-width:100%;
24304
+ width:100%;
24305
+ box-sizing:border-box;
24306
+ display:block !important
24307
+ }
24274
24308
  .tab[aria-selected="true"] {
24275
24309
  background:var(--aksara-bg-subtle);
24276
24310
  color:var(--aksara-text-body)
@@ -24303,6 +24337,41 @@ img,svg,video {
24303
24337
  border-color:rgb(var(--aksara-primary)/.22);
24304
24338
  color:rgb(var(--aksara-primary))
24305
24339
  }
24340
+ .tabs-segmented {
24341
+ display:flex;
24342
+ gap:2px;
24343
+ padding:3px;
24344
+ background:var(--aksara-bg-subtle);
24345
+ border-radius:var(--aksara-radius-lg,1rem);
24346
+ border:1px solid var(--aksara-border-subtle)
24347
+ }
24348
+ .tabs-segmented .tab {
24349
+ flex:1;
24350
+ display:inline-flex;
24351
+ align-items:center;
24352
+ justify-content:center;
24353
+ gap:.375rem;
24354
+ border-radius:calc(var(--aksara-radius-lg,1rem) - 2px);
24355
+ padding:.4375rem .75rem;
24356
+ font:inherit;
24357
+ font-size:.8125rem;
24358
+ font-weight:600;
24359
+ color:var(--aksara-text-subtle);
24360
+ transition:all .15s ease-in-out;
24361
+ border:0;
24362
+ background:transparent;
24363
+ text-decoration:none;
24364
+ cursor:pointer
24365
+ }
24366
+ .tabs-segmented .tab:hover {
24367
+ color:var(--aksara-text-body)
24368
+ }
24369
+ .tabs-segmented .tab.active,.tabs-segmented .tab[aria-selected="true"] {
24370
+ background:var(--aksara-surface);
24371
+ color:var(--aksara-text-body);
24372
+ box-shadow:var(--aksara-shadow-sm);
24373
+ font-weight:700
24374
+ }
24306
24375
  .toast {
24307
24376
  display:none;
24308
24377
  max-width:24rem;
package/dist/aksara.d.ts CHANGED
@@ -189,7 +189,9 @@ export declare class Tabs extends Component {
189
189
  constructor(element: HTMLElement, options?: TabsOptions);
190
190
  updateOptions(options?: TabsOptions): this;
191
191
  activateInitial(): void;
192
- show(tab: HTMLElement): this;
192
+ getSliderTrack(): HTMLElement | null;
193
+ bindSliderGestures(): void;
194
+ show(tab: HTMLElement, animate?: boolean): this;
193
195
  }
194
196
  export declare class Toast extends Component {
195
197
  static componentName: string;
@@ -990,6 +990,7 @@ export class Tabs extends Component {
990
990
  this.show(next);
991
991
  }
992
992
  });
993
+ this.bindSliderGestures();
993
994
  this.activateInitial();
994
995
  }
995
996
  updateOptions(options = {}) {
@@ -1029,29 +1030,128 @@ export class Tabs extends Component {
1029
1030
  if (initial)
1030
1031
  this.show(initial);
1031
1032
  }
1032
- show(tab) {
1033
+ getSliderTrack() {
1034
+ const wrapper = this.element.closest(".tabs-wrapper") ||
1035
+ this.element.closest(".feed-main-col, main, .page-container, [data-tabs-wrapper]") ||
1036
+ this.element.parentElement;
1037
+ return (wrapper?.querySelector(".tabs-slider-track, [data-tabs-slider]") ||
1038
+ document.querySelector(".tabs-slider-track, [data-tabs-slider]"));
1039
+ }
1040
+ bindSliderGestures() {
1041
+ const track = this.getSliderTrack();
1042
+ if (!track || track.__sliderGesturesBound)
1043
+ return;
1044
+ track.__sliderGesturesBound = true;
1045
+ let startX = 0;
1046
+ let startY = 0;
1047
+ let deltaX = 0;
1048
+ let deltaY = 0;
1049
+ let isSwiping = false;
1050
+ let isVertical = false;
1051
+ track.addEventListener("touchstart", (e) => {
1052
+ if (e.touches.length !== 1)
1053
+ return;
1054
+ const target = e.target;
1055
+ if (target?.closest("input, textarea, select, [contenteditable='true'], video, audio, .carousel, .bento-carousel, .media-grid-scroll, .map-canvas-container, [data-prevent-swipe]")) {
1056
+ return;
1057
+ }
1058
+ const touch = e.touches[0];
1059
+ startX = touch.clientX;
1060
+ startY = touch.clientY;
1061
+ deltaX = 0;
1062
+ deltaY = 0;
1063
+ isSwiping = false;
1064
+ isVertical = false;
1065
+ }, { passive: true });
1066
+ track.addEventListener("touchmove", (e) => {
1067
+ if (e.touches.length !== 1 || isVertical)
1068
+ return;
1069
+ const touch = e.touches[0];
1070
+ deltaX = touch.clientX - startX;
1071
+ deltaY = touch.clientY - startY;
1072
+ if (!isSwiping) {
1073
+ if (Math.abs(deltaY) > 8 && Math.abs(deltaY) > Math.abs(deltaX)) {
1074
+ isVertical = true;
1075
+ return;
1076
+ }
1077
+ if (Math.abs(deltaX) > 8 && Math.abs(deltaX) > Math.abs(deltaY)) {
1078
+ isSwiping = true;
1079
+ }
1080
+ }
1081
+ if (isSwiping) {
1082
+ if (e.cancelable)
1083
+ e.preventDefault();
1084
+ const tabs = Array.from(this.element.querySelectorAll(this.tabSelector));
1085
+ const maxIdx = Math.max(0, tabs.length - 1);
1086
+ let currentDelta = deltaX;
1087
+ const currentIdx = tabs.findIndex((t) => t.classList.contains("active"));
1088
+ const safeIdx = currentIdx >= 0 ? currentIdx : 0;
1089
+ if ((safeIdx === 0 && currentDelta > 0) || (safeIdx === maxIdx && currentDelta < 0)) {
1090
+ currentDelta = currentDelta * 0.3;
1091
+ }
1092
+ track.style.transition = "none";
1093
+ track.style.transform = `translate3d(calc(-${safeIdx * 100}% + ${currentDelta}px), 0, 0)`;
1094
+ }
1095
+ }, { passive: false });
1096
+ const onTouchEnd = () => {
1097
+ if (!isSwiping)
1098
+ return;
1099
+ isSwiping = false;
1100
+ const tabs = Array.from(this.element.querySelectorAll(this.tabSelector));
1101
+ const maxIdx = Math.max(0, tabs.length - 1);
1102
+ const currentIdx = tabs.findIndex((t) => t.classList.contains("active"));
1103
+ const safeIdx = currentIdx >= 0 ? currentIdx : 0;
1104
+ track.style.transition = "transform 0.3s cubic-bezier(0.25, 1, 0.5, 1)";
1105
+ if (deltaX < -50 && safeIdx < maxIdx) {
1106
+ this.show(tabs[safeIdx + 1]);
1107
+ }
1108
+ else if (deltaX > 50 && safeIdx > 0) {
1109
+ this.show(tabs[safeIdx - 1]);
1110
+ }
1111
+ else {
1112
+ track.style.transform = `translate3d(-${safeIdx * 100}%, 0, 0)`;
1113
+ }
1114
+ };
1115
+ track.addEventListener("touchend", onTouchEnd, { passive: true });
1116
+ track.addEventListener("touchcancel", onTouchEnd, { passive: true });
1117
+ }
1118
+ show(tab, animate = true) {
1033
1119
  const selector = tab.getAttribute("data-tabs");
1034
1120
  const panel = selector ? document.querySelector(selector) : null;
1035
1121
  if (!panel)
1036
1122
  return this;
1037
- this.element.querySelectorAll(this.tabSelector).forEach((item) => {
1123
+ const tabs = Array.from(this.element.querySelectorAll(this.tabSelector));
1124
+ const targetIndex = tabs.indexOf(tab);
1125
+ const track = this.getSliderTrack();
1126
+ const isSlider = Boolean(track && track.contains(panel));
1127
+ tabs.forEach((item) => {
1038
1128
  const selected = item === tab;
1039
1129
  item.setAttribute("role", item.getAttribute("role") || "tab");
1040
1130
  item.setAttribute("aria-selected", String(selected));
1131
+ item.classList.toggle("active", selected);
1041
1132
  item.tabIndex = selected ? 0 : -1;
1042
1133
  const targetSelector = item.getAttribute("data-tabs");
1043
1134
  const target = targetSelector ? document.querySelector(targetSelector) : null;
1044
- if (target)
1045
- target.hidden = !selected;
1135
+ if (target) {
1136
+ target.classList.toggle("active", selected);
1137
+ if (!isSlider) {
1138
+ target.hidden = !selected;
1139
+ }
1140
+ }
1046
1141
  });
1047
- if (this.panelSelector) {
1142
+ if (this.panelSelector && !isSlider) {
1048
1143
  document.querySelectorAll(this.panelSelector).forEach((item) => {
1049
1144
  if (item !== panel)
1050
1145
  item.hidden = true;
1051
1146
  });
1052
1147
  }
1053
1148
  panel.setAttribute("role", panel.getAttribute("role") || "tabpanel");
1054
- emit(this.element, "tabs:show", { tab, panel, instance: this });
1149
+ if (isSlider && track && targetIndex >= 0) {
1150
+ track.style.transition = animate ? "transform 0.3s cubic-bezier(0.25, 1, 0.5, 1)" : "none";
1151
+ track.style.transform = `translate3d(-${targetIndex * 100}%, 0, 0)`;
1152
+ }
1153
+ tab.scrollIntoView({ behavior: "smooth", inline: "center", block: "nearest" });
1154
+ emit(this.element, "tabs:show", { tab, panel, index: targetIndex, instance: this });
1055
1155
  return this;
1056
1156
  }
1057
1157
  }
package/dist/aksara.js CHANGED
@@ -991,6 +991,7 @@ class Tabs extends Component {
991
991
  this.show(next);
992
992
  }
993
993
  });
994
+ this.bindSliderGestures();
994
995
  this.activateInitial();
995
996
  }
996
997
  updateOptions(options = {}) {
@@ -1030,29 +1031,128 @@ class Tabs extends Component {
1030
1031
  if (initial)
1031
1032
  this.show(initial);
1032
1033
  }
1033
- show(tab) {
1034
+ getSliderTrack() {
1035
+ const wrapper = this.element.closest(".tabs-wrapper") ||
1036
+ this.element.closest(".feed-main-col, main, .page-container, [data-tabs-wrapper]") ||
1037
+ this.element.parentElement;
1038
+ return (wrapper?.querySelector(".tabs-slider-track, [data-tabs-slider]") ||
1039
+ document.querySelector(".tabs-slider-track, [data-tabs-slider]"));
1040
+ }
1041
+ bindSliderGestures() {
1042
+ const track = this.getSliderTrack();
1043
+ if (!track || track.__sliderGesturesBound)
1044
+ return;
1045
+ track.__sliderGesturesBound = true;
1046
+ let startX = 0;
1047
+ let startY = 0;
1048
+ let deltaX = 0;
1049
+ let deltaY = 0;
1050
+ let isSwiping = false;
1051
+ let isVertical = false;
1052
+ track.addEventListener("touchstart", (e) => {
1053
+ if (e.touches.length !== 1)
1054
+ return;
1055
+ const target = e.target;
1056
+ if (target?.closest("input, textarea, select, [contenteditable='true'], video, audio, .carousel, .bento-carousel, .media-grid-scroll, .map-canvas-container, [data-prevent-swipe]")) {
1057
+ return;
1058
+ }
1059
+ const touch = e.touches[0];
1060
+ startX = touch.clientX;
1061
+ startY = touch.clientY;
1062
+ deltaX = 0;
1063
+ deltaY = 0;
1064
+ isSwiping = false;
1065
+ isVertical = false;
1066
+ }, { passive: true });
1067
+ track.addEventListener("touchmove", (e) => {
1068
+ if (e.touches.length !== 1 || isVertical)
1069
+ return;
1070
+ const touch = e.touches[0];
1071
+ deltaX = touch.clientX - startX;
1072
+ deltaY = touch.clientY - startY;
1073
+ if (!isSwiping) {
1074
+ if (Math.abs(deltaY) > 8 && Math.abs(deltaY) > Math.abs(deltaX)) {
1075
+ isVertical = true;
1076
+ return;
1077
+ }
1078
+ if (Math.abs(deltaX) > 8 && Math.abs(deltaX) > Math.abs(deltaY)) {
1079
+ isSwiping = true;
1080
+ }
1081
+ }
1082
+ if (isSwiping) {
1083
+ if (e.cancelable)
1084
+ e.preventDefault();
1085
+ const tabs = Array.from(this.element.querySelectorAll(this.tabSelector));
1086
+ const maxIdx = Math.max(0, tabs.length - 1);
1087
+ let currentDelta = deltaX;
1088
+ const currentIdx = tabs.findIndex((t) => t.classList.contains("active"));
1089
+ const safeIdx = currentIdx >= 0 ? currentIdx : 0;
1090
+ if ((safeIdx === 0 && currentDelta > 0) || (safeIdx === maxIdx && currentDelta < 0)) {
1091
+ currentDelta = currentDelta * 0.3;
1092
+ }
1093
+ track.style.transition = "none";
1094
+ track.style.transform = `translate3d(calc(-${safeIdx * 100}% + ${currentDelta}px), 0, 0)`;
1095
+ }
1096
+ }, { passive: false });
1097
+ const onTouchEnd = () => {
1098
+ if (!isSwiping)
1099
+ return;
1100
+ isSwiping = false;
1101
+ const tabs = Array.from(this.element.querySelectorAll(this.tabSelector));
1102
+ const maxIdx = Math.max(0, tabs.length - 1);
1103
+ const currentIdx = tabs.findIndex((t) => t.classList.contains("active"));
1104
+ const safeIdx = currentIdx >= 0 ? currentIdx : 0;
1105
+ track.style.transition = "transform 0.3s cubic-bezier(0.25, 1, 0.5, 1)";
1106
+ if (deltaX < -50 && safeIdx < maxIdx) {
1107
+ this.show(tabs[safeIdx + 1]);
1108
+ }
1109
+ else if (deltaX > 50 && safeIdx > 0) {
1110
+ this.show(tabs[safeIdx - 1]);
1111
+ }
1112
+ else {
1113
+ track.style.transform = `translate3d(-${safeIdx * 100}%, 0, 0)`;
1114
+ }
1115
+ };
1116
+ track.addEventListener("touchend", onTouchEnd, { passive: true });
1117
+ track.addEventListener("touchcancel", onTouchEnd, { passive: true });
1118
+ }
1119
+ show(tab, animate = true) {
1034
1120
  const selector = tab.getAttribute("data-tabs");
1035
1121
  const panel = selector ? document.querySelector(selector) : null;
1036
1122
  if (!panel)
1037
1123
  return this;
1038
- this.element.querySelectorAll(this.tabSelector).forEach((item) => {
1124
+ const tabs = Array.from(this.element.querySelectorAll(this.tabSelector));
1125
+ const targetIndex = tabs.indexOf(tab);
1126
+ const track = this.getSliderTrack();
1127
+ const isSlider = Boolean(track && track.contains(panel));
1128
+ tabs.forEach((item) => {
1039
1129
  const selected = item === tab;
1040
1130
  item.setAttribute("role", item.getAttribute("role") || "tab");
1041
1131
  item.setAttribute("aria-selected", String(selected));
1132
+ item.classList.toggle("active", selected);
1042
1133
  item.tabIndex = selected ? 0 : -1;
1043
1134
  const targetSelector = item.getAttribute("data-tabs");
1044
1135
  const target = targetSelector ? document.querySelector(targetSelector) : null;
1045
- if (target)
1046
- target.hidden = !selected;
1136
+ if (target) {
1137
+ target.classList.toggle("active", selected);
1138
+ if (!isSlider) {
1139
+ target.hidden = !selected;
1140
+ }
1141
+ }
1047
1142
  });
1048
- if (this.panelSelector) {
1143
+ if (this.panelSelector && !isSlider) {
1049
1144
  document.querySelectorAll(this.panelSelector).forEach((item) => {
1050
1145
  if (item !== panel)
1051
1146
  item.hidden = true;
1052
1147
  });
1053
1148
  }
1054
1149
  panel.setAttribute("role", panel.getAttribute("role") || "tabpanel");
1055
- emit(this.element, "tabs:show", { tab, panel, instance: this });
1150
+ if (isSlider && track && targetIndex >= 0) {
1151
+ track.style.transition = animate ? "transform 0.3s cubic-bezier(0.25, 1, 0.5, 1)" : "none";
1152
+ track.style.transform = `translate3d(-${targetIndex * 100}%, 0, 0)`;
1153
+ }
1154
+ tab.scrollIntoView({ behavior: "smooth", inline: "center", block: "nearest" });
1155
+ emit(this.element, "tabs:show", { tab, panel, index: targetIndex, instance: this });
1056
1156
  return this;
1057
1157
  }
1058
1158
  }