@kkcompany/app-ui 0.1.30 → 0.1.32

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/index.cjs CHANGED
@@ -342,6 +342,8 @@ var Banner_default = Banner;
342
342
  //#endregion
343
343
  //#region src/Dropdown.tsx
344
344
  /** @jsxImportSource @emotion/react */
345
+ const dropdownBorderRadius = "4px";
346
+ const itemRowElemMarginXSm = "16px";
345
347
  const dropdownStyle = __emotion_react.css`
346
348
  .text-truncate {
347
349
  display: block;
@@ -411,7 +413,7 @@ const dropdownStyle = __emotion_react.css`
411
413
 
412
414
  .kks-dropdown__menu {
413
415
  width: 92%;
414
- border-radius: $dropdown-border-radius;
416
+ border-radius: ${dropdownBorderRadius};
415
417
  background: #fff;
416
418
  overflow: hidden;
417
419
  }
@@ -421,7 +423,7 @@ const dropdownStyle = __emotion_react.css`
421
423
  border-bottom: 1px solid #e9ecef;
422
424
 
423
425
  .kks-dropdown__menu-title {
424
- margin-left: $item-row-elem-margin-x-sm;
426
+ margin-left: ${itemRowElemMarginXSm};
425
427
  font-size: 1.25rem;
426
428
  }
427
429
 
@@ -429,7 +431,7 @@ const dropdownStyle = __emotion_react.css`
429
431
  display: flex;
430
432
  align-items: center;
431
433
  margin-left: auto;
432
- padding: 0 $item-row-elem-margin-x-sm;
434
+ padding: 0 ${itemRowElemMarginXSm};
433
435
  height: 100%;
434
436
  outline: none;
435
437
  }
@@ -449,7 +451,7 @@ const dropdownStyle = __emotion_react.css`
449
451
  overflow-y: auto;
450
452
 
451
453
  .kks-dropdown__menu-link {
452
- padding: 10px $item-row-elem-margin-x-sm;
454
+ padding: 10px ${itemRowElemMarginXSm};
453
455
  text-decoration: none;
454
456
  color: #000;
455
457
  outline: none;
@@ -712,6 +714,404 @@ const Icon = ({ style = {}, src }) => /* @__PURE__ */ (0, __emotion_react_jsx_ru
712
714
  ] });
713
715
  var Icon_default = Icon;
714
716
 
717
+ //#endregion
718
+ //#region src/Modal/styles.ts
719
+ const modalStyle = __emotion_react.css`
720
+ :where(&) {
721
+ position: fixed;
722
+ top: 0;
723
+ right: 0;
724
+ bottom: 0;
725
+ left: 0;
726
+ z-index: 1000;
727
+ padding: 1rem;
728
+ overflow-x: hidden;
729
+ overflow-y: auto;
730
+ visibility: hidden;
731
+ opacity: 0;
732
+ background-color: rgba(0, 0, 0, 0.7);
733
+ transition: opacity 0.3s;
734
+ }
735
+
736
+ :where(&.modal--show),
737
+ :where(&.modal--shown) {
738
+ display: flex;
739
+ justify-content: center;
740
+ visibility: visible;
741
+ opacity: 1;
742
+ }
743
+
744
+ & :where(.modal__content) {
745
+ width: 100%;
746
+ max-width: 700px;
747
+ margin: auto 0;
748
+ }
749
+ `;
750
+
751
+ //#endregion
752
+ //#region src/Modal/index.tsx
753
+ const MODAL_STATE = {
754
+ SHOW: "show",
755
+ SHOWN: "shown",
756
+ HIDE: "hide",
757
+ HIDDEN: "hidden"
758
+ };
759
+ const isShowing = (state) => state === MODAL_STATE.SHOW || state === MODAL_STATE.SHOWN;
760
+ const Modal = (0, react.memo)((0, react.forwardRef)(function Modal$1({ children, className = "", isHideModalOnClickOverlay = true, isShow, onHidden = () => {}, onShown = () => {}, onTriggerHiding = () => {} }, ref) {
761
+ const [visualState, setVisualState] = (0, react.useState)(MODAL_STATE.HIDDEN);
762
+ const shouldLockBody = isShowing(visualState);
763
+ if (isShow && !isShowing(visualState)) setVisualState(MODAL_STATE.SHOW);
764
+ else if (!isShow && isShowing(visualState)) setVisualState(MODAL_STATE.HIDE);
765
+ (0, react.useLayoutEffect)(() => {
766
+ if (!shouldLockBody) return;
767
+ const previousOverflow = document.body.style.overflow;
768
+ document.body.style.overflow = "hidden";
769
+ return () => {
770
+ document.body.style.overflow = previousOverflow;
771
+ };
772
+ }, [shouldLockBody]);
773
+ const previousVisualState = (0, react.useRef)(visualState);
774
+ (0, react.useLayoutEffect)(() => {
775
+ if (previousVisualState.current === visualState) return;
776
+ previousVisualState.current = visualState;
777
+ if (visualState === MODAL_STATE.SHOWN) onShown();
778
+ else if (visualState === MODAL_STATE.HIDDEN) onHidden();
779
+ }, [
780
+ visualState,
781
+ onShown,
782
+ onHidden
783
+ ]);
784
+ const handleAnimationEnd = (0, react.useCallback)(() => {
785
+ setVisualState((previousState) => {
786
+ if (previousState === MODAL_STATE.SHOW) return MODAL_STATE.SHOWN;
787
+ if (previousState === MODAL_STATE.HIDE) return MODAL_STATE.HIDDEN;
788
+ return previousState;
789
+ });
790
+ }, []);
791
+ const handleClick = (0, react.useCallback)((event) => {
792
+ if (event.target.getAttribute("data-dismiss") === "modal") onTriggerHiding();
793
+ }, [onTriggerHiding]);
794
+ const handleKeyDown = (0, react.useCallback)((event) => {
795
+ if (event.key !== "Enter" && event.key !== " ") return;
796
+ if (event.target.getAttribute("data-dismiss") !== "modal") return;
797
+ event.preventDefault();
798
+ onTriggerHiding();
799
+ }, [onTriggerHiding]);
800
+ return /* @__PURE__ */ (0, __emotion_react_jsx_runtime.jsx)("div", {
801
+ css: modalStyle,
802
+ ref,
803
+ role: "presentation",
804
+ className: `modal modal--${visualState} ${className}`,
805
+ onClick: handleClick,
806
+ onKeyDown: handleKeyDown,
807
+ onAnimationEnd: handleAnimationEnd,
808
+ onTransitionEnd: handleAnimationEnd,
809
+ "data-dismiss": isHideModalOnClickOverlay ? "modal" : void 0,
810
+ children: /* @__PURE__ */ (0, __emotion_react_jsx_runtime.jsx)("div", {
811
+ className: "modal__content",
812
+ children
813
+ })
814
+ });
815
+ }));
816
+ var Modal_default = Modal;
817
+
818
+ //#endregion
819
+ //#region src/Pagination/PaginationLink.tsx
820
+ const PaginationLink = ({ routeData, nextLink: NextLinkComponent, children, ...anchorProps }) => {
821
+ const anchor = /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
822
+ ...anchorProps,
823
+ href: NextLinkComponent ? void 0 : routeData.route,
824
+ children
825
+ });
826
+ if (!NextLinkComponent) return anchor;
827
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(NextLinkComponent, {
828
+ route: routeData.route,
829
+ params: routeData.routeParams,
830
+ ...routeData.routeOptions || {},
831
+ children: anchor
832
+ });
833
+ };
834
+ var PaginationLink_default = PaginationLink;
835
+
836
+ //#endregion
837
+ //#region src/Pagination/ArrowButton.tsx
838
+ const ArrowButton = ({ type, routeData, nextLink, disabled = false, onClick }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PaginationLink_default, {
839
+ className: `kks-pagination__btn kks-pagination__arrow-btn kks-pagination__arrow-btn--${type}${disabled ? " kks-pagination__arrow-btn--hidden" : ""}`,
840
+ routeData,
841
+ nextLink,
842
+ rel: type,
843
+ "aria-label": type === "prev" ? "Previous page" : "Next page",
844
+ onClick,
845
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: `kks-pagination__icon kks-pagination__icon-${type}` })
846
+ });
847
+ var ArrowButton_default = ArrowButton;
848
+
849
+ //#endregion
850
+ //#region src/Pagination/DotsButton.tsx
851
+ const DotsButton = ({ dots }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
852
+ className: "kks-pagination__dots",
853
+ children: dots
854
+ });
855
+ var DotsButton_default = DotsButton;
856
+
857
+ //#endregion
858
+ //#region src/Pagination/NumberButton.tsx
859
+ const NumberButton = ({ totalPages, pageNumber, routeData, nextLink, active = false, onClick }) => {
860
+ if (active) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
861
+ className: "kks-pagination__btn kks-pagination__num-btn kks-pagination__num-btn--active",
862
+ "data-total-pages": ` / ${totalPages}`,
863
+ "aria-current": "page",
864
+ children: pageNumber
865
+ });
866
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PaginationLink_default, {
867
+ className: "kks-pagination__btn kks-pagination__num-btn",
868
+ routeData,
869
+ nextLink,
870
+ onClick,
871
+ children: pageNumber
872
+ });
873
+ };
874
+ var NumberButton_default = NumberButton;
875
+
876
+ //#endregion
877
+ //#region src/Pagination/styles.ts
878
+ const paginationStyle = __emotion_react.css`
879
+ :where(&) {
880
+ display: inline-block;
881
+ }
882
+
883
+ & :where(.kks-pagination__content) {
884
+ display: flex;
885
+ margin: 0;
886
+ padding-left: 0;
887
+ list-style: none;
888
+ }
889
+
890
+ & :where(.kks-pagination__num-item:not(.kks-pagination__num-item--current)) {
891
+ display: none;
892
+ }
893
+
894
+ & :where(.kks-pagination__btn) {
895
+ display: block;
896
+ margin: 0 12px;
897
+ padding: 0.64rem 0.8rem;
898
+ border-radius: 3px;
899
+ font-size: 1rem;
900
+ line-height: 1;
901
+ user-select: none;
902
+ }
903
+
904
+ & :where(.kks-pagination__num-btn) {
905
+ text-decoration: none;
906
+ color: #666;
907
+ }
908
+
909
+ & :where(.kks-pagination__num-btn)::after {
910
+ content: attr(data-total-pages);
911
+ }
912
+
913
+ & :where(.kks-pagination__arrow-btn--prev) {
914
+ margin-left: 0;
915
+ }
916
+
917
+ & :where(.kks-pagination__arrow-btn--next) {
918
+ margin-right: 0;
919
+ }
920
+
921
+ & :where(.kks-pagination__arrow-btn--hidden) {
922
+ display: none;
923
+ }
924
+
925
+ & :where(.kks-pagination__icon) {
926
+ display: block;
927
+ width: 1rem;
928
+ height: 1rem;
929
+ background-size: contain;
930
+ }
931
+
932
+ & :where(.kks-pagination__icon-prev) {
933
+ background-image: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIj8+PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCAxMjkgMTI5IiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCAxMjkgMTI5IiB3aWR0aD0iNTEycHgiIGhlaWdodD0iNTEycHgiPjxnIHRyYW5zZm9ybT0ibWF0cml4KC0xIDAgMCAxIDEyOSAwKSI+PGc+ICAgIDxwYXRoIGQ9Im00MC40LDEyMS4zYy0wLjgsMC44LTEuOCwxLjItMi45LDEuMnMtMi4xLTAuNC0yLjktMS4yYy0xLjYtMS42LTEuNi00LjIgMC01LjhsNTEtNTEtNTEtNTFjLTEuNi0xLjYtMS42LTQuMiAwLTUuOCAxLjYtMS42IDQuMi0xLjYgNS44LDBsNTMuOSw1My45YzEuNiwxLjYgMS42LDQuMiAwLDUuOGwtNTMuOSw1My45eiIgZGF0YS1vcmlnaW5hbD0iIzAwMDAwMCIgY2xhc3M9ImFjdGl2ZS1wYXRoIiBkYXRhLW9sZF9jb2xvcj0iIzAwMDAwMCIgZmlsbD0iIzY2NjY2NiIvPiAgPC9nPjwvZz4gPC9zdmc+");
934
+ }
935
+
936
+ & :where(.kks-pagination__icon-next) {
937
+ background-image: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIj8+PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCAxMjkgMTI5IiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCAxMjkgMTI5IiB3aWR0aD0iNTEycHgiIGhlaWdodD0iNTEycHgiPjxnPjxnPiAgICA8cGF0aCBkPSJtNDAuNCwxMjEuM2MtMC44LDAuOC0xLjgsMS4yLTIuOSwxLjJzLTIuMS0wLjQtMi45LTEuMmMtMS42LTEuNi0xLjYtNC4yIDAtNS44bDUxLTUxLTUxLTUxYy0xLjYtMS42LTEuNi00LjIgMC01LjggMS42LTEuNiA0LjItMS42IDUuOCwwbDUzLjksNTMuOWMxLjYsMS42IDEuNiw0LjIgMCw1LjhsLTUzLjksNTMuOXoiIGRhdGEtb3JpZ2luYWw9IiMwMDAwMDAiIGNsYXNzPSJhY3RpdmUtcGF0aCIgZGF0YS1vbGRfY29sb3I9IiMwMDAwMDAiIGZpbGw9IiM2NjY2NjYiLz4gIDwvZz48L2c+IDwvc3ZnPg==");
938
+ }
939
+
940
+ & :where(.kks-pagination__dots) {
941
+ display: block;
942
+ margin: 0 12px;
943
+ padding: 0.64rem 0;
944
+ font-size: 1rem;
945
+ line-height: 50%;
946
+ color: #aaa;
947
+ user-select: none;
948
+ }
949
+
950
+ @media (min-width: 768px) {
951
+ & :where(.kks-pagination__num-item:not(.kks-pagination__num-item--current)) {
952
+ display: block;
953
+ }
954
+
955
+ & :where(.kks-pagination__btn),
956
+ & :where(.kks-pagination__dots) {
957
+ margin: 0 6px;
958
+ }
959
+
960
+ & :where(.kks-pagination__num-btn)::after {
961
+ content: "";
962
+ }
963
+
964
+ & :where(.kks-pagination__num-btn:hover:not(.kks-pagination__num-btn--active)),
965
+ & :where(.kks-pagination__arrow-btn:hover) {
966
+ background: rgba(0, 161, 179, 0.1);
967
+ }
968
+
969
+ & :where(.kks-pagination__num-btn--active) {
970
+ font-weight: 500;
971
+ color: #fff;
972
+ background: #00a1b3;
973
+ }
974
+
975
+ & :where(.kks-pagination__arrow-btn--prev) {
976
+ margin-left: 0;
977
+ }
978
+
979
+ & :where(.kks-pagination__arrow-btn--next) {
980
+ margin-right: 0;
981
+ }
982
+ }
983
+ `;
984
+
985
+ //#endregion
986
+ //#region src/Pagination/index.tsx
987
+ const DEFAULT_CURRENT_PAGE = 1;
988
+ const DEFAULT_PAGE_NEIGHBOURS = 2;
989
+ const ROUTE_URL_REPLACE_STRING = "$(pageNumber)";
990
+ const range = (start, end) => Array.from({ length: end - start + 1 }, (_, index) => start + index);
991
+ const getPageNumbers = (currentPage, pageNeighbours, totalPages) => {
992
+ const numberBlocks = pageNeighbours * 2 + 3;
993
+ if (totalPages <= numberBlocks + 2) return range(1, totalPages);
994
+ const startPage = Math.max(2, currentPage - pageNeighbours);
995
+ const endPage = Math.min(totalPages - 1, currentPage + pageNeighbours);
996
+ let pages = range(startPage, endPage);
997
+ const hasLeftDots = startPage > 2;
998
+ const hasRightDots = totalPages - endPage > 1;
999
+ const offset = numberBlocks - (pages.length + 2);
1000
+ if (hasLeftDots && !hasRightDots) pages = [
1001
+ "...",
1002
+ ...range(startPage - offset, startPage - 1),
1003
+ ...pages
1004
+ ];
1005
+ else if (!hasLeftDots && hasRightDots) pages = [
1006
+ ...pages,
1007
+ ...range(endPage + 1, endPage + offset),
1008
+ "..."
1009
+ ];
1010
+ else pages = [
1011
+ "...",
1012
+ ...pages,
1013
+ "..."
1014
+ ];
1015
+ return [
1016
+ 1,
1017
+ ...pages,
1018
+ totalPages
1019
+ ];
1020
+ };
1021
+ const toPositiveInt = (value, fallback) => {
1022
+ const parsed = Number(value);
1023
+ return parsed > 0 ? parsed : fallback;
1024
+ };
1025
+ const toValidPage = (value, totalPages) => {
1026
+ const page = Number(value);
1027
+ return Number.isNaN(page) || page < 1 || page > totalPages ? DEFAULT_CURRENT_PAGE : page;
1028
+ };
1029
+ const normalizePaginationParams = ({ totalCount: rawTotalCount, perPageCount: rawPerPageCount, currentPage: rawCurrentPage = DEFAULT_CURRENT_PAGE, pageNeighbours: rawPageNeighbours = DEFAULT_PAGE_NEIGHBOURS }) => {
1030
+ const totalCount = Number(rawTotalCount);
1031
+ const perPageCount = Number(rawPerPageCount);
1032
+ const totalPages = totalCount && perPageCount ? Math.ceil(totalCount / perPageCount) : 1;
1033
+ return {
1034
+ totalCount,
1035
+ perPageCount,
1036
+ totalPages,
1037
+ currentPage: toValidPage(rawCurrentPage, totalPages),
1038
+ pageNeighbours: toPositiveInt(rawPageNeighbours, DEFAULT_PAGE_NEIGHBOURS)
1039
+ };
1040
+ };
1041
+ const renderEllipsis = (ellipsis, index) => /* @__PURE__ */ (0, __emotion_react_jsx_runtime.jsx)("li", {
1042
+ className: "kks-pagination__num-item",
1043
+ children: /* @__PURE__ */ (0, __emotion_react_jsx_runtime.jsx)(DotsButton_default, { dots: ellipsis })
1044
+ }, `dots-${index}`);
1045
+ const Pagination = (props) => {
1046
+ const { totalCount, perPageCount, totalPages, currentPage, pageNeighbours } = normalizePaginationParams(props);
1047
+ const { route = null, routeParams = null, routeOptions = null, nextRouter = null, nextLink = null, className = "", onPageChange = () => {} } = props;
1048
+ if (totalPages === 1) return null;
1049
+ const getRouteData = (page) => {
1050
+ if (!route) return { route: "#" };
1051
+ const nextRouteParams = routeParams ? Object.fromEntries(Object.entries(routeParams).map(([key, value]) => [key, value === ROUTE_URL_REPLACE_STRING ? page : value])) : null;
1052
+ if (nextRouteParams && !Object.values(routeParams).includes(ROUTE_URL_REPLACE_STRING)) nextRouteParams.page = page;
1053
+ return {
1054
+ route: route.replace(ROUTE_URL_REPLACE_STRING, String(page)),
1055
+ routeParams: nextRouteParams,
1056
+ routeOptions
1057
+ };
1058
+ };
1059
+ const handleClick = (page, routeData) => (event) => {
1060
+ event.preventDefault();
1061
+ const isNavigable = routeData.route !== "#";
1062
+ onPageChange({
1063
+ currentPage: page,
1064
+ totalPages,
1065
+ totalCount,
1066
+ perPageCount,
1067
+ route: isNavigable ? routeData.route : null,
1068
+ ...isNavigable ? routeData : {}
1069
+ });
1070
+ if (nextRouter && isNavigable) nextRouter.pushRoute(routeData.route, routeData.routeParams || {}, routeData.routeOptions || {});
1071
+ };
1072
+ const previousPage = currentPage === 1 ? 1 : currentPage - 1;
1073
+ const nextPage = currentPage === totalPages ? totalPages : currentPage + 1;
1074
+ const renderArrow = (type, page, disabled) => {
1075
+ const routeData = getRouteData(page);
1076
+ return /* @__PURE__ */ (0, __emotion_react_jsx_runtime.jsx)(ArrowButton_default, {
1077
+ type,
1078
+ routeData,
1079
+ nextLink,
1080
+ disabled,
1081
+ onClick: handleClick(page, routeData)
1082
+ });
1083
+ };
1084
+ const renderPageNumber = (page) => {
1085
+ const routeData = getRouteData(page);
1086
+ const isCurrentPage = page === currentPage;
1087
+ return /* @__PURE__ */ (0, __emotion_react_jsx_runtime.jsx)("li", {
1088
+ className: `kks-pagination__num-item${isCurrentPage ? " kks-pagination__num-item--current" : ""}`,
1089
+ children: /* @__PURE__ */ (0, __emotion_react_jsx_runtime.jsx)(NumberButton_default, {
1090
+ totalPages,
1091
+ pageNumber: page,
1092
+ routeData,
1093
+ nextLink,
1094
+ active: isCurrentPage,
1095
+ onClick: handleClick(page, routeData)
1096
+ })
1097
+ }, `page-${page}`);
1098
+ };
1099
+ return /* @__PURE__ */ (0, __emotion_react_jsx_runtime.jsx)("nav", {
1100
+ css: paginationStyle,
1101
+ className: `kks-pagination ${className}`,
1102
+ "aria-label": "Page navigation",
1103
+ children: /* @__PURE__ */ (0, __emotion_react_jsx_runtime.jsxs)("ul", {
1104
+ className: "kks-pagination__content",
1105
+ children: [
1106
+ /* @__PURE__ */ (0, __emotion_react_jsx_runtime.jsx)("li", { children: renderArrow("prev", previousPage, currentPage === 1) }),
1107
+ getPageNumbers(currentPage, pageNeighbours, totalPages).map((page, index) => typeof page === "number" ? renderPageNumber(page) : renderEllipsis(page, index)),
1108
+ /* @__PURE__ */ (0, __emotion_react_jsx_runtime.jsx)("li", { children: renderArrow("next", nextPage, currentPage === totalPages) })
1109
+ ]
1110
+ })
1111
+ });
1112
+ };
1113
+ var Pagination_default = Pagination;
1114
+
715
1115
  //#endregion
716
1116
  //#region src/icons/see-more.svg
717
1117
  var see_more_default = "PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNCIgaGVpZ2h0PSI4IiBmaWxsPSJub25lIiB2aWV3Qm94PSIwIDAgMTQgOCI+CiAgPHBhdGggc3Ryb2tlPSIjY2NjIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS13aWR0aD0iMS41IiBkPSJNLjc1Ljc1IDYuNyA2LjY4cS4wNy4wNi4xNCAwbTAgMHEwIDAgMCAwWm0wIDBMMTIuNzUuNzUiLz4KPC9zdmc+";
@@ -1083,6 +1483,8 @@ exports.Banner = Banner_default;
1083
1483
  exports.Dropdown = Dropdown_default;
1084
1484
  exports.HiddenToggle = HiddenToggle_default;
1085
1485
  exports.Icon = Icon_default;
1486
+ exports.Modal = Modal_default;
1487
+ exports.Pagination = Pagination_default;
1086
1488
  exports.SeeMore = SeeMore_default;
1087
1489
  exports.VideoItem = VideoItem_default;
1088
1490
  exports.VideoThumbnail = VideoThumbnail_default;
package/dist/index.d.cts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as react0 from "react";
2
- import { ElementType, MouseEventHandler, ReactElement, ReactNode } from "react";
3
- import * as _emotion_react_jsx_runtime0 from "@emotion/react/jsx-runtime";
2
+ import { ComponentType, ElementType, MouseEventHandler, ReactElement, ReactNode } from "react";
3
+ import * as _emotion_react_jsx_runtime2 from "@emotion/react/jsx-runtime";
4
4
  import { CSSObject } from "@emotion/react";
5
5
  import Link from "next/link";
6
6
 
@@ -65,7 +65,7 @@ declare const HiddenToggle: ({
65
65
  defaultEnabled?: boolean;
66
66
  children: any;
67
67
  onChange: any;
68
- }) => _emotion_react_jsx_runtime0.JSX.Element;
68
+ }) => _emotion_react_jsx_runtime2.JSX.Element;
69
69
  //#endregion
70
70
  //#region src/Icon.d.ts
71
71
  declare const Icon: ({
@@ -74,7 +74,55 @@ declare const Icon: ({
74
74
  }: {
75
75
  style?: {};
76
76
  src: any;
77
- }) => _emotion_react_jsx_runtime0.JSX.Element;
77
+ }) => _emotion_react_jsx_runtime2.JSX.Element;
78
+ //#endregion
79
+ //#region src/Modal/index.d.ts
80
+ type ModalProps = {
81
+ children: ReactNode;
82
+ className?: string;
83
+ isHideModalOnClickOverlay?: boolean;
84
+ isShow: boolean;
85
+ onHidden?: () => void;
86
+ onShown?: () => void;
87
+ onTriggerHiding?: () => void;
88
+ };
89
+ declare const Modal: react0.MemoExoticComponent<react0.ForwardRefExoticComponent<ModalProps & react0.RefAttributes<HTMLDivElement>>>;
90
+ //#endregion
91
+ //#region src/Pagination/types.d.ts
92
+ type RouteParams = Record<string, unknown>;
93
+ type RouteOptions = Record<string, unknown>;
94
+ type NextLinkProps = {
95
+ route: string;
96
+ params?: RouteParams | null;
97
+ children: ReactNode;
98
+ } & RouteOptions;
99
+ type NextLink = ComponentType<NextLinkProps>;
100
+ //#endregion
101
+ //#region src/Pagination/index.d.ts
102
+ type PaginationProps = {
103
+ totalCount: number | string;
104
+ perPageCount: number | string;
105
+ currentPage?: number | string;
106
+ pageNeighbours?: number | string;
107
+ route?: string | null;
108
+ routeParams?: RouteParams | null;
109
+ routeOptions?: RouteOptions | null;
110
+ nextRouter?: {
111
+ pushRoute: (route: string, routeParams?: RouteParams, routeOptions?: RouteOptions) => void;
112
+ } | null;
113
+ nextLink?: NextLink | null;
114
+ className?: string;
115
+ onPageChange?: (page: {
116
+ currentPage: number;
117
+ totalPages: number;
118
+ totalCount: number;
119
+ perPageCount: number;
120
+ route: string | null;
121
+ routeParams?: RouteParams | null;
122
+ routeOptions?: RouteOptions | null;
123
+ }) => void;
124
+ };
125
+ declare const Pagination: (props: PaginationProps) => ReactElement | null;
78
126
  //#endregion
79
127
  //#region src/SeeMore.d.ts
80
128
  declare const defaultMessages: {
@@ -90,7 +138,7 @@ declare const SeeMore: ({
90
138
  style?: CSSObject;
91
139
  messages?: typeof defaultMessages;
92
140
  children?: ReactNode;
93
- }) => _emotion_react_jsx_runtime0.JSX.Element;
141
+ }) => _emotion_react_jsx_runtime2.JSX.Element;
94
142
  //#endregion
95
143
  //#region src/VideoItem.d.ts
96
144
  type VideoDetail = {
@@ -154,6 +202,6 @@ declare const VideoThumbnail: ({
154
202
  Image?: ElementType;
155
203
  };
156
204
  onClick?: MouseEventHandler<HTMLAnchorElement>;
157
- }) => _emotion_react_jsx_runtime0.JSX.Element;
205
+ }) => _emotion_react_jsx_runtime2.JSX.Element;
158
206
  //#endregion
159
- export { Banner, Dropdown, HiddenToggle, Icon, SeeMore, VideoItem, VideoThumbnail };
207
+ export { Banner, Dropdown, HiddenToggle, Icon, Modal, Pagination, SeeMore, VideoItem, VideoThumbnail };
package/dist/index.d.mts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as react0 from "react";
2
- import { ElementType, MouseEventHandler, ReactElement, ReactNode } from "react";
3
- import * as _emotion_react_jsx_runtime2 from "@emotion/react/jsx-runtime";
2
+ import { ComponentType, ElementType, MouseEventHandler, ReactElement, ReactNode } from "react";
3
+ import * as _emotion_react_jsx_runtime0 from "@emotion/react/jsx-runtime";
4
4
  import { CSSObject } from "@emotion/react";
5
5
  import Link from "next/link";
6
6
 
@@ -65,7 +65,7 @@ declare const HiddenToggle: ({
65
65
  defaultEnabled?: boolean;
66
66
  children: any;
67
67
  onChange: any;
68
- }) => _emotion_react_jsx_runtime2.JSX.Element;
68
+ }) => _emotion_react_jsx_runtime0.JSX.Element;
69
69
  //#endregion
70
70
  //#region src/Icon.d.ts
71
71
  declare const Icon: ({
@@ -74,7 +74,55 @@ declare const Icon: ({
74
74
  }: {
75
75
  style?: {};
76
76
  src: any;
77
- }) => _emotion_react_jsx_runtime2.JSX.Element;
77
+ }) => _emotion_react_jsx_runtime0.JSX.Element;
78
+ //#endregion
79
+ //#region src/Modal/index.d.ts
80
+ type ModalProps = {
81
+ children: ReactNode;
82
+ className?: string;
83
+ isHideModalOnClickOverlay?: boolean;
84
+ isShow: boolean;
85
+ onHidden?: () => void;
86
+ onShown?: () => void;
87
+ onTriggerHiding?: () => void;
88
+ };
89
+ declare const Modal: react0.MemoExoticComponent<react0.ForwardRefExoticComponent<ModalProps & react0.RefAttributes<HTMLDivElement>>>;
90
+ //#endregion
91
+ //#region src/Pagination/types.d.ts
92
+ type RouteParams = Record<string, unknown>;
93
+ type RouteOptions = Record<string, unknown>;
94
+ type NextLinkProps = {
95
+ route: string;
96
+ params?: RouteParams | null;
97
+ children: ReactNode;
98
+ } & RouteOptions;
99
+ type NextLink = ComponentType<NextLinkProps>;
100
+ //#endregion
101
+ //#region src/Pagination/index.d.ts
102
+ type PaginationProps = {
103
+ totalCount: number | string;
104
+ perPageCount: number | string;
105
+ currentPage?: number | string;
106
+ pageNeighbours?: number | string;
107
+ route?: string | null;
108
+ routeParams?: RouteParams | null;
109
+ routeOptions?: RouteOptions | null;
110
+ nextRouter?: {
111
+ pushRoute: (route: string, routeParams?: RouteParams, routeOptions?: RouteOptions) => void;
112
+ } | null;
113
+ nextLink?: NextLink | null;
114
+ className?: string;
115
+ onPageChange?: (page: {
116
+ currentPage: number;
117
+ totalPages: number;
118
+ totalCount: number;
119
+ perPageCount: number;
120
+ route: string | null;
121
+ routeParams?: RouteParams | null;
122
+ routeOptions?: RouteOptions | null;
123
+ }) => void;
124
+ };
125
+ declare const Pagination: (props: PaginationProps) => ReactElement | null;
78
126
  //#endregion
79
127
  //#region src/SeeMore.d.ts
80
128
  declare const defaultMessages: {
@@ -90,7 +138,7 @@ declare const SeeMore: ({
90
138
  style?: CSSObject;
91
139
  messages?: typeof defaultMessages;
92
140
  children?: ReactNode;
93
- }) => _emotion_react_jsx_runtime2.JSX.Element;
141
+ }) => _emotion_react_jsx_runtime0.JSX.Element;
94
142
  //#endregion
95
143
  //#region src/VideoItem.d.ts
96
144
  type VideoDetail = {
@@ -154,6 +202,6 @@ declare const VideoThumbnail: ({
154
202
  Image?: ElementType;
155
203
  };
156
204
  onClick?: MouseEventHandler<HTMLAnchorElement>;
157
- }) => _emotion_react_jsx_runtime2.JSX.Element;
205
+ }) => _emotion_react_jsx_runtime0.JSX.Element;
158
206
  //#endregion
159
- export { Banner, Dropdown, HiddenToggle, Icon, SeeMore, VideoItem, VideoThumbnail };
207
+ export { Banner, Dropdown, HiddenToggle, Icon, Modal, Pagination, SeeMore, VideoItem, VideoThumbnail };
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { Children, forwardRef, useCallback, useEffect, useImperativeHandle, useLayoutEffect, useMemo, useReducer, useRef, useState } from "react";
1
+ import { Children, forwardRef, memo, useCallback, useEffect, useImperativeHandle, useLayoutEffect, useMemo, useReducer, useRef, useState } from "react";
2
2
  import { jsx } from "react/jsx-runtime";
3
3
  import { jsx as jsx$1, jsxs } from "@emotion/react/jsx-runtime";
4
4
  import { css } from "@emotion/react";
@@ -342,6 +342,8 @@ var Banner_default = Banner;
342
342
  //#endregion
343
343
  //#region src/Dropdown.tsx
344
344
  /** @jsxImportSource @emotion/react */
345
+ const dropdownBorderRadius = "4px";
346
+ const itemRowElemMarginXSm = "16px";
345
347
  const dropdownStyle = css`
346
348
  .text-truncate {
347
349
  display: block;
@@ -411,7 +413,7 @@ const dropdownStyle = css`
411
413
 
412
414
  .kks-dropdown__menu {
413
415
  width: 92%;
414
- border-radius: $dropdown-border-radius;
416
+ border-radius: ${dropdownBorderRadius};
415
417
  background: #fff;
416
418
  overflow: hidden;
417
419
  }
@@ -421,7 +423,7 @@ const dropdownStyle = css`
421
423
  border-bottom: 1px solid #e9ecef;
422
424
 
423
425
  .kks-dropdown__menu-title {
424
- margin-left: $item-row-elem-margin-x-sm;
426
+ margin-left: ${itemRowElemMarginXSm};
425
427
  font-size: 1.25rem;
426
428
  }
427
429
 
@@ -429,7 +431,7 @@ const dropdownStyle = css`
429
431
  display: flex;
430
432
  align-items: center;
431
433
  margin-left: auto;
432
- padding: 0 $item-row-elem-margin-x-sm;
434
+ padding: 0 ${itemRowElemMarginXSm};
433
435
  height: 100%;
434
436
  outline: none;
435
437
  }
@@ -449,7 +451,7 @@ const dropdownStyle = css`
449
451
  overflow-y: auto;
450
452
 
451
453
  .kks-dropdown__menu-link {
452
- padding: 10px $item-row-elem-margin-x-sm;
454
+ padding: 10px ${itemRowElemMarginXSm};
453
455
  text-decoration: none;
454
456
  color: #000;
455
457
  outline: none;
@@ -712,6 +714,404 @@ const Icon = ({ style = {}, src }) => /* @__PURE__ */ jsx$1("span", { css: [
712
714
  ] });
713
715
  var Icon_default = Icon;
714
716
 
717
+ //#endregion
718
+ //#region src/Modal/styles.ts
719
+ const modalStyle = css`
720
+ :where(&) {
721
+ position: fixed;
722
+ top: 0;
723
+ right: 0;
724
+ bottom: 0;
725
+ left: 0;
726
+ z-index: 1000;
727
+ padding: 1rem;
728
+ overflow-x: hidden;
729
+ overflow-y: auto;
730
+ visibility: hidden;
731
+ opacity: 0;
732
+ background-color: rgba(0, 0, 0, 0.7);
733
+ transition: opacity 0.3s;
734
+ }
735
+
736
+ :where(&.modal--show),
737
+ :where(&.modal--shown) {
738
+ display: flex;
739
+ justify-content: center;
740
+ visibility: visible;
741
+ opacity: 1;
742
+ }
743
+
744
+ & :where(.modal__content) {
745
+ width: 100%;
746
+ max-width: 700px;
747
+ margin: auto 0;
748
+ }
749
+ `;
750
+
751
+ //#endregion
752
+ //#region src/Modal/index.tsx
753
+ const MODAL_STATE = {
754
+ SHOW: "show",
755
+ SHOWN: "shown",
756
+ HIDE: "hide",
757
+ HIDDEN: "hidden"
758
+ };
759
+ const isShowing = (state) => state === MODAL_STATE.SHOW || state === MODAL_STATE.SHOWN;
760
+ const Modal = memo(forwardRef(function Modal$1({ children, className = "", isHideModalOnClickOverlay = true, isShow, onHidden = () => {}, onShown = () => {}, onTriggerHiding = () => {} }, ref) {
761
+ const [visualState, setVisualState] = useState(MODAL_STATE.HIDDEN);
762
+ const shouldLockBody = isShowing(visualState);
763
+ if (isShow && !isShowing(visualState)) setVisualState(MODAL_STATE.SHOW);
764
+ else if (!isShow && isShowing(visualState)) setVisualState(MODAL_STATE.HIDE);
765
+ useLayoutEffect(() => {
766
+ if (!shouldLockBody) return;
767
+ const previousOverflow = document.body.style.overflow;
768
+ document.body.style.overflow = "hidden";
769
+ return () => {
770
+ document.body.style.overflow = previousOverflow;
771
+ };
772
+ }, [shouldLockBody]);
773
+ const previousVisualState = useRef(visualState);
774
+ useLayoutEffect(() => {
775
+ if (previousVisualState.current === visualState) return;
776
+ previousVisualState.current = visualState;
777
+ if (visualState === MODAL_STATE.SHOWN) onShown();
778
+ else if (visualState === MODAL_STATE.HIDDEN) onHidden();
779
+ }, [
780
+ visualState,
781
+ onShown,
782
+ onHidden
783
+ ]);
784
+ const handleAnimationEnd = useCallback(() => {
785
+ setVisualState((previousState) => {
786
+ if (previousState === MODAL_STATE.SHOW) return MODAL_STATE.SHOWN;
787
+ if (previousState === MODAL_STATE.HIDE) return MODAL_STATE.HIDDEN;
788
+ return previousState;
789
+ });
790
+ }, []);
791
+ const handleClick = useCallback((event) => {
792
+ if (event.target.getAttribute("data-dismiss") === "modal") onTriggerHiding();
793
+ }, [onTriggerHiding]);
794
+ const handleKeyDown = useCallback((event) => {
795
+ if (event.key !== "Enter" && event.key !== " ") return;
796
+ if (event.target.getAttribute("data-dismiss") !== "modal") return;
797
+ event.preventDefault();
798
+ onTriggerHiding();
799
+ }, [onTriggerHiding]);
800
+ return /* @__PURE__ */ jsx$1("div", {
801
+ css: modalStyle,
802
+ ref,
803
+ role: "presentation",
804
+ className: `modal modal--${visualState} ${className}`,
805
+ onClick: handleClick,
806
+ onKeyDown: handleKeyDown,
807
+ onAnimationEnd: handleAnimationEnd,
808
+ onTransitionEnd: handleAnimationEnd,
809
+ "data-dismiss": isHideModalOnClickOverlay ? "modal" : void 0,
810
+ children: /* @__PURE__ */ jsx$1("div", {
811
+ className: "modal__content",
812
+ children
813
+ })
814
+ });
815
+ }));
816
+ var Modal_default = Modal;
817
+
818
+ //#endregion
819
+ //#region src/Pagination/PaginationLink.tsx
820
+ const PaginationLink = ({ routeData, nextLink: NextLinkComponent, children, ...anchorProps }) => {
821
+ const anchor = /* @__PURE__ */ jsx("a", {
822
+ ...anchorProps,
823
+ href: NextLinkComponent ? void 0 : routeData.route,
824
+ children
825
+ });
826
+ if (!NextLinkComponent) return anchor;
827
+ return /* @__PURE__ */ jsx(NextLinkComponent, {
828
+ route: routeData.route,
829
+ params: routeData.routeParams,
830
+ ...routeData.routeOptions || {},
831
+ children: anchor
832
+ });
833
+ };
834
+ var PaginationLink_default = PaginationLink;
835
+
836
+ //#endregion
837
+ //#region src/Pagination/ArrowButton.tsx
838
+ const ArrowButton = ({ type, routeData, nextLink, disabled = false, onClick }) => /* @__PURE__ */ jsx(PaginationLink_default, {
839
+ className: `kks-pagination__btn kks-pagination__arrow-btn kks-pagination__arrow-btn--${type}${disabled ? " kks-pagination__arrow-btn--hidden" : ""}`,
840
+ routeData,
841
+ nextLink,
842
+ rel: type,
843
+ "aria-label": type === "prev" ? "Previous page" : "Next page",
844
+ onClick,
845
+ children: /* @__PURE__ */ jsx("span", { className: `kks-pagination__icon kks-pagination__icon-${type}` })
846
+ });
847
+ var ArrowButton_default = ArrowButton;
848
+
849
+ //#endregion
850
+ //#region src/Pagination/DotsButton.tsx
851
+ const DotsButton = ({ dots }) => /* @__PURE__ */ jsx("span", {
852
+ className: "kks-pagination__dots",
853
+ children: dots
854
+ });
855
+ var DotsButton_default = DotsButton;
856
+
857
+ //#endregion
858
+ //#region src/Pagination/NumberButton.tsx
859
+ const NumberButton = ({ totalPages, pageNumber, routeData, nextLink, active = false, onClick }) => {
860
+ if (active) return /* @__PURE__ */ jsx("span", {
861
+ className: "kks-pagination__btn kks-pagination__num-btn kks-pagination__num-btn--active",
862
+ "data-total-pages": ` / ${totalPages}`,
863
+ "aria-current": "page",
864
+ children: pageNumber
865
+ });
866
+ return /* @__PURE__ */ jsx(PaginationLink_default, {
867
+ className: "kks-pagination__btn kks-pagination__num-btn",
868
+ routeData,
869
+ nextLink,
870
+ onClick,
871
+ children: pageNumber
872
+ });
873
+ };
874
+ var NumberButton_default = NumberButton;
875
+
876
+ //#endregion
877
+ //#region src/Pagination/styles.ts
878
+ const paginationStyle = css`
879
+ :where(&) {
880
+ display: inline-block;
881
+ }
882
+
883
+ & :where(.kks-pagination__content) {
884
+ display: flex;
885
+ margin: 0;
886
+ padding-left: 0;
887
+ list-style: none;
888
+ }
889
+
890
+ & :where(.kks-pagination__num-item:not(.kks-pagination__num-item--current)) {
891
+ display: none;
892
+ }
893
+
894
+ & :where(.kks-pagination__btn) {
895
+ display: block;
896
+ margin: 0 12px;
897
+ padding: 0.64rem 0.8rem;
898
+ border-radius: 3px;
899
+ font-size: 1rem;
900
+ line-height: 1;
901
+ user-select: none;
902
+ }
903
+
904
+ & :where(.kks-pagination__num-btn) {
905
+ text-decoration: none;
906
+ color: #666;
907
+ }
908
+
909
+ & :where(.kks-pagination__num-btn)::after {
910
+ content: attr(data-total-pages);
911
+ }
912
+
913
+ & :where(.kks-pagination__arrow-btn--prev) {
914
+ margin-left: 0;
915
+ }
916
+
917
+ & :where(.kks-pagination__arrow-btn--next) {
918
+ margin-right: 0;
919
+ }
920
+
921
+ & :where(.kks-pagination__arrow-btn--hidden) {
922
+ display: none;
923
+ }
924
+
925
+ & :where(.kks-pagination__icon) {
926
+ display: block;
927
+ width: 1rem;
928
+ height: 1rem;
929
+ background-size: contain;
930
+ }
931
+
932
+ & :where(.kks-pagination__icon-prev) {
933
+ background-image: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIj8+PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCAxMjkgMTI5IiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCAxMjkgMTI5IiB3aWR0aD0iNTEycHgiIGhlaWdodD0iNTEycHgiPjxnIHRyYW5zZm9ybT0ibWF0cml4KC0xIDAgMCAxIDEyOSAwKSI+PGc+ICAgIDxwYXRoIGQ9Im00MC40LDEyMS4zYy0wLjgsMC44LTEuOCwxLjItMi45LDEuMnMtMi4xLTAuNC0yLjktMS4yYy0xLjYtMS42LTEuNi00LjIgMC01LjhsNTEtNTEtNTEtNTFjLTEuNi0xLjYtMS42LTQuMiAwLTUuOCAxLjYtMS42IDQuMi0xLjYgNS44LDBsNTMuOSw1My45YzEuNiwxLjYgMS42LDQuMiAwLDUuOGwtNTMuOSw1My45eiIgZGF0YS1vcmlnaW5hbD0iIzAwMDAwMCIgY2xhc3M9ImFjdGl2ZS1wYXRoIiBkYXRhLW9sZF9jb2xvcj0iIzAwMDAwMCIgZmlsbD0iIzY2NjY2NiIvPiAgPC9nPjwvZz4gPC9zdmc+");
934
+ }
935
+
936
+ & :where(.kks-pagination__icon-next) {
937
+ background-image: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIj8+PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCAxMjkgMTI5IiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCAxMjkgMTI5IiB3aWR0aD0iNTEycHgiIGhlaWdodD0iNTEycHgiPjxnPjxnPiAgICA8cGF0aCBkPSJtNDAuNCwxMjEuM2MtMC44LDAuOC0xLjgsMS4yLTIuOSwxLjJzLTIuMS0wLjQtMi45LTEuMmMtMS42LTEuNi0xLjYtNC4yIDAtNS44bDUxLTUxLTUxLTUxYy0xLjYtMS42LTEuNi00LjIgMC01LjggMS42LTEuNiA0LjItMS42IDUuOCwwbDUzLjksNTMuOWMxLjYsMS42IDEuNiw0LjIgMCw1LjhsLTUzLjksNTMuOXoiIGRhdGEtb3JpZ2luYWw9IiMwMDAwMDAiIGNsYXNzPSJhY3RpdmUtcGF0aCIgZGF0YS1vbGRfY29sb3I9IiMwMDAwMDAiIGZpbGw9IiM2NjY2NjYiLz4gIDwvZz48L2c+IDwvc3ZnPg==");
938
+ }
939
+
940
+ & :where(.kks-pagination__dots) {
941
+ display: block;
942
+ margin: 0 12px;
943
+ padding: 0.64rem 0;
944
+ font-size: 1rem;
945
+ line-height: 50%;
946
+ color: #aaa;
947
+ user-select: none;
948
+ }
949
+
950
+ @media (min-width: 768px) {
951
+ & :where(.kks-pagination__num-item:not(.kks-pagination__num-item--current)) {
952
+ display: block;
953
+ }
954
+
955
+ & :where(.kks-pagination__btn),
956
+ & :where(.kks-pagination__dots) {
957
+ margin: 0 6px;
958
+ }
959
+
960
+ & :where(.kks-pagination__num-btn)::after {
961
+ content: "";
962
+ }
963
+
964
+ & :where(.kks-pagination__num-btn:hover:not(.kks-pagination__num-btn--active)),
965
+ & :where(.kks-pagination__arrow-btn:hover) {
966
+ background: rgba(0, 161, 179, 0.1);
967
+ }
968
+
969
+ & :where(.kks-pagination__num-btn--active) {
970
+ font-weight: 500;
971
+ color: #fff;
972
+ background: #00a1b3;
973
+ }
974
+
975
+ & :where(.kks-pagination__arrow-btn--prev) {
976
+ margin-left: 0;
977
+ }
978
+
979
+ & :where(.kks-pagination__arrow-btn--next) {
980
+ margin-right: 0;
981
+ }
982
+ }
983
+ `;
984
+
985
+ //#endregion
986
+ //#region src/Pagination/index.tsx
987
+ const DEFAULT_CURRENT_PAGE = 1;
988
+ const DEFAULT_PAGE_NEIGHBOURS = 2;
989
+ const ROUTE_URL_REPLACE_STRING = "$(pageNumber)";
990
+ const range = (start, end) => Array.from({ length: end - start + 1 }, (_, index) => start + index);
991
+ const getPageNumbers = (currentPage, pageNeighbours, totalPages) => {
992
+ const numberBlocks = pageNeighbours * 2 + 3;
993
+ if (totalPages <= numberBlocks + 2) return range(1, totalPages);
994
+ const startPage = Math.max(2, currentPage - pageNeighbours);
995
+ const endPage = Math.min(totalPages - 1, currentPage + pageNeighbours);
996
+ let pages = range(startPage, endPage);
997
+ const hasLeftDots = startPage > 2;
998
+ const hasRightDots = totalPages - endPage > 1;
999
+ const offset = numberBlocks - (pages.length + 2);
1000
+ if (hasLeftDots && !hasRightDots) pages = [
1001
+ "...",
1002
+ ...range(startPage - offset, startPage - 1),
1003
+ ...pages
1004
+ ];
1005
+ else if (!hasLeftDots && hasRightDots) pages = [
1006
+ ...pages,
1007
+ ...range(endPage + 1, endPage + offset),
1008
+ "..."
1009
+ ];
1010
+ else pages = [
1011
+ "...",
1012
+ ...pages,
1013
+ "..."
1014
+ ];
1015
+ return [
1016
+ 1,
1017
+ ...pages,
1018
+ totalPages
1019
+ ];
1020
+ };
1021
+ const toPositiveInt = (value, fallback) => {
1022
+ const parsed = Number(value);
1023
+ return parsed > 0 ? parsed : fallback;
1024
+ };
1025
+ const toValidPage = (value, totalPages) => {
1026
+ const page = Number(value);
1027
+ return Number.isNaN(page) || page < 1 || page > totalPages ? DEFAULT_CURRENT_PAGE : page;
1028
+ };
1029
+ const normalizePaginationParams = ({ totalCount: rawTotalCount, perPageCount: rawPerPageCount, currentPage: rawCurrentPage = DEFAULT_CURRENT_PAGE, pageNeighbours: rawPageNeighbours = DEFAULT_PAGE_NEIGHBOURS }) => {
1030
+ const totalCount = Number(rawTotalCount);
1031
+ const perPageCount = Number(rawPerPageCount);
1032
+ const totalPages = totalCount && perPageCount ? Math.ceil(totalCount / perPageCount) : 1;
1033
+ return {
1034
+ totalCount,
1035
+ perPageCount,
1036
+ totalPages,
1037
+ currentPage: toValidPage(rawCurrentPage, totalPages),
1038
+ pageNeighbours: toPositiveInt(rawPageNeighbours, DEFAULT_PAGE_NEIGHBOURS)
1039
+ };
1040
+ };
1041
+ const renderEllipsis = (ellipsis, index) => /* @__PURE__ */ jsx$1("li", {
1042
+ className: "kks-pagination__num-item",
1043
+ children: /* @__PURE__ */ jsx$1(DotsButton_default, { dots: ellipsis })
1044
+ }, `dots-${index}`);
1045
+ const Pagination = (props) => {
1046
+ const { totalCount, perPageCount, totalPages, currentPage, pageNeighbours } = normalizePaginationParams(props);
1047
+ const { route = null, routeParams = null, routeOptions = null, nextRouter = null, nextLink = null, className = "", onPageChange = () => {} } = props;
1048
+ if (totalPages === 1) return null;
1049
+ const getRouteData = (page) => {
1050
+ if (!route) return { route: "#" };
1051
+ const nextRouteParams = routeParams ? Object.fromEntries(Object.entries(routeParams).map(([key, value]) => [key, value === ROUTE_URL_REPLACE_STRING ? page : value])) : null;
1052
+ if (nextRouteParams && !Object.values(routeParams).includes(ROUTE_URL_REPLACE_STRING)) nextRouteParams.page = page;
1053
+ return {
1054
+ route: route.replace(ROUTE_URL_REPLACE_STRING, String(page)),
1055
+ routeParams: nextRouteParams,
1056
+ routeOptions
1057
+ };
1058
+ };
1059
+ const handleClick = (page, routeData) => (event) => {
1060
+ event.preventDefault();
1061
+ const isNavigable = routeData.route !== "#";
1062
+ onPageChange({
1063
+ currentPage: page,
1064
+ totalPages,
1065
+ totalCount,
1066
+ perPageCount,
1067
+ route: isNavigable ? routeData.route : null,
1068
+ ...isNavigable ? routeData : {}
1069
+ });
1070
+ if (nextRouter && isNavigable) nextRouter.pushRoute(routeData.route, routeData.routeParams || {}, routeData.routeOptions || {});
1071
+ };
1072
+ const previousPage = currentPage === 1 ? 1 : currentPage - 1;
1073
+ const nextPage = currentPage === totalPages ? totalPages : currentPage + 1;
1074
+ const renderArrow = (type, page, disabled) => {
1075
+ const routeData = getRouteData(page);
1076
+ return /* @__PURE__ */ jsx$1(ArrowButton_default, {
1077
+ type,
1078
+ routeData,
1079
+ nextLink,
1080
+ disabled,
1081
+ onClick: handleClick(page, routeData)
1082
+ });
1083
+ };
1084
+ const renderPageNumber = (page) => {
1085
+ const routeData = getRouteData(page);
1086
+ const isCurrentPage = page === currentPage;
1087
+ return /* @__PURE__ */ jsx$1("li", {
1088
+ className: `kks-pagination__num-item${isCurrentPage ? " kks-pagination__num-item--current" : ""}`,
1089
+ children: /* @__PURE__ */ jsx$1(NumberButton_default, {
1090
+ totalPages,
1091
+ pageNumber: page,
1092
+ routeData,
1093
+ nextLink,
1094
+ active: isCurrentPage,
1095
+ onClick: handleClick(page, routeData)
1096
+ })
1097
+ }, `page-${page}`);
1098
+ };
1099
+ return /* @__PURE__ */ jsx$1("nav", {
1100
+ css: paginationStyle,
1101
+ className: `kks-pagination ${className}`,
1102
+ "aria-label": "Page navigation",
1103
+ children: /* @__PURE__ */ jsxs("ul", {
1104
+ className: "kks-pagination__content",
1105
+ children: [
1106
+ /* @__PURE__ */ jsx$1("li", { children: renderArrow("prev", previousPage, currentPage === 1) }),
1107
+ getPageNumbers(currentPage, pageNeighbours, totalPages).map((page, index) => typeof page === "number" ? renderPageNumber(page) : renderEllipsis(page, index)),
1108
+ /* @__PURE__ */ jsx$1("li", { children: renderArrow("next", nextPage, currentPage === totalPages) })
1109
+ ]
1110
+ })
1111
+ });
1112
+ };
1113
+ var Pagination_default = Pagination;
1114
+
715
1115
  //#endregion
716
1116
  //#region src/icons/see-more.svg
717
1117
  var see_more_default = "PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNCIgaGVpZ2h0PSI4IiBmaWxsPSJub25lIiB2aWV3Qm94PSIwIDAgMTQgOCI+CiAgPHBhdGggc3Ryb2tlPSIjY2NjIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS13aWR0aD0iMS41IiBkPSJNLjc1Ljc1IDYuNyA2LjY4cS4wNy4wNi4xNCAwbTAgMHEwIDAgMCAwWm0wIDBMMTIuNzUuNzUiLz4KPC9zdmc+";
@@ -1079,4 +1479,4 @@ const VideoItem = ({ style, href = "", thumbnailHref = href, imageUrl = "", data
1079
1479
  var VideoItem_default = VideoItem;
1080
1480
 
1081
1481
  //#endregion
1082
- export { Banner_default as Banner, Dropdown_default as Dropdown, HiddenToggle_default as HiddenToggle, Icon_default as Icon, SeeMore_default as SeeMore, VideoItem_default as VideoItem, VideoThumbnail_default as VideoThumbnail };
1482
+ export { Banner_default as Banner, Dropdown_default as Dropdown, HiddenToggle_default as HiddenToggle, Icon_default as Icon, Modal_default as Modal, Pagination_default as Pagination, SeeMore_default as SeeMore, VideoItem_default as VideoItem, VideoThumbnail_default as VideoThumbnail };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kkcompany/app-ui",
3
- "version": "0.1.30",
3
+ "version": "0.1.32",
4
4
  "module": "dist/index.mjs",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.mts",
@@ -19,7 +19,8 @@
19
19
  "license": "MIT",
20
20
  "scripts": {
21
21
  "dev": "next",
22
- "build": "tsdown --format esm --format cjs"
22
+ "build": "tsdown --format esm --format cjs",
23
+ "test": "vitest run"
23
24
  },
24
25
  "dependencies": {
25
26
  "@emotion/react": "^11.14.0"
@@ -30,10 +31,13 @@
30
31
  },
31
32
  "devDependencies": {
32
33
  "@biomejs/biome": "^2.3.5",
34
+ "@testing-library/react": "^13.4.0",
33
35
  "binary-base64-loader": "^1.0.0",
36
+ "jsdom": "^26.1.0",
34
37
  "next": "^16.0.2",
35
38
  "rollup-plugin-base64": "^1.0.1",
36
39
  "tsdown": "^0.16.4",
37
- "typescript": "^5.9.3"
40
+ "typescript": "^5.9.3",
41
+ "vitest": "^4.1.0"
38
42
  }
39
43
  }