@kodiak-finance/orderly-ui-notification 2.8.19 → 2.8.21-alpha.0

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.mjs CHANGED
@@ -1,8 +1,8 @@
1
- import { useMemo, useState, useEffect, useCallback } from 'react';
1
+ import React2, { useMemo, useState, useEffect, useCallback, useRef, useLayoutEffect } from 'react';
2
2
  import { useTranslation } from '@kodiak-finance/orderly-i18n';
3
- import { Flex, Text, cn, ChevronDownIcon, ScrollArea, Divider, Icon, ExtensionSlot, ExtensionPositionEnum, ChevronLeftIcon, ChevronRightIcon } from '@kodiak-finance/orderly-ui';
3
+ import { Flex, Text, cn, ChevronDownIcon, ScrollArea, Divider, useEmblaCarousel, Dialog, DialogContent, DialogHeader, DialogTitle, DialogBody, Icon, ExtensionSlot, ExtensionPositionEnum, ChevronLeftIcon, ChevronRightIcon, useScreen, CloseIcon } from '@kodiak-finance/orderly-ui';
4
4
  import { AnnouncementType, EMPTY_LIST } from '@kodiak-finance/orderly-types';
5
- import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
5
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
6
6
  import { UTCDate, UTCDateMini } from '@date-fns/utc';
7
7
  import { differenceInHours, differenceInMinutes, format } from 'date-fns';
8
8
  import { produce } from 'immer';
@@ -860,14 +860,470 @@ var AnnouncementCenterPage = (props) => {
860
860
  return;
861
861
  props.routerAdapter?.onRouteChange({
862
862
  href: url,
863
- name: url,
864
- target: "_blank"
863
+ name: url
865
864
  });
866
865
  }
867
866
  }
868
867
  );
869
868
  };
869
+ var useMarqueeOnce = (opts) => {
870
+ const {
871
+ isActive,
872
+ pxPerSec = 60,
873
+ startDelayMs = 500,
874
+ endDelayMs = 500,
875
+ fallbackStayMs = 2500,
876
+ onFinish
877
+ } = opts;
878
+ const containerRef = useRef(null);
879
+ const contentRef = useRef(null);
880
+ const rafRef = useRef(null);
881
+ const stopAll = () => {
882
+ if (rafRef.current) {
883
+ cancelAnimationFrame(rafRef.current);
884
+ }
885
+ rafRef.current = null;
886
+ };
887
+ const timers = useRef([]);
888
+ const clearTimers = () => {
889
+ timers.current?.forEach((id) => {
890
+ if (id) {
891
+ clearTimeout(id);
892
+ }
893
+ });
894
+ timers.current = [];
895
+ };
896
+ const [overflow, setOverflow] = useState(false);
897
+ const [delta, setDelta] = useState(0);
898
+ useLayoutEffect(() => {
899
+ const c = containerRef.current;
900
+ const t = contentRef.current;
901
+ if (!c || !t) {
902
+ return;
903
+ }
904
+ const update = () => {
905
+ const cw = c.clientWidth;
906
+ const tw = t.scrollWidth;
907
+ const need = Math.max(0, tw - cw);
908
+ setOverflow(need > 0);
909
+ setDelta(need);
910
+ };
911
+ update();
912
+ const ro = new ResizeObserver(update);
913
+ ro.observe(c);
914
+ ro.observe(t);
915
+ return () => {
916
+ ro.disconnect();
917
+ };
918
+ }, []);
919
+ useEffect(() => {
920
+ if (!isActive) {
921
+ stopAll();
922
+ clearTimers();
923
+ if (contentRef.current) {
924
+ contentRef.current.style.transform = "translate3d(0, 0, 0)";
925
+ }
926
+ return;
927
+ }
928
+ const run = async () => {
929
+ stopAll();
930
+ clearTimers();
931
+ if (!overflow || delta <= 0) {
932
+ const id = setTimeout(() => {
933
+ onFinish?.();
934
+ }, fallbackStayMs);
935
+ timers.current.push(id);
936
+ return;
937
+ }
938
+ const startId = setTimeout(() => {
939
+ const distance = delta;
940
+ const durationMs = distance / pxPerSec * 1e3;
941
+ const el = contentRef.current;
942
+ if (!el) {
943
+ return;
944
+ }
945
+ let startTs = 0;
946
+ const startX = 0;
947
+ const endX = -distance;
948
+ const step = (ts) => {
949
+ if (!startTs) {
950
+ startTs = ts;
951
+ }
952
+ const progress = Math.min(1, (ts - startTs) / durationMs);
953
+ const x = startX + (endX - startX) * progress;
954
+ el.style.transform = `translate3d(${x}px, 0, 0)`;
955
+ if (progress < 1) {
956
+ rafRef.current = requestAnimationFrame(step);
957
+ } else {
958
+ const endId = setTimeout(() => {
959
+ onFinish?.();
960
+ }, endDelayMs);
961
+ timers.current.push(endId);
962
+ }
963
+ };
964
+ rafRef.current = requestAnimationFrame(step);
965
+ }, startDelayMs);
966
+ timers.current.push(startId);
967
+ };
968
+ run();
969
+ return () => {
970
+ stopAll();
971
+ clearTimers();
972
+ };
973
+ }, [
974
+ isActive,
975
+ overflow,
976
+ delta,
977
+ pxPerSec,
978
+ startDelayMs,
979
+ endDelayMs,
980
+ fallbackStayMs,
981
+ onFinish
982
+ ]);
983
+ return { containerRef, contentRef, overflow, delta };
984
+ };
985
+ var usePrevNextButtons = (emblaApi, onButtonClick) => {
986
+ const [prevBtnDisabled, setPrevBtnDisabled] = useState(true);
987
+ const [nextBtnDisabled, setNextBtnDisabled] = useState(true);
988
+ const onPrevButtonClick = useCallback(() => {
989
+ if (!emblaApi) {
990
+ return;
991
+ }
992
+ emblaApi.scrollPrev();
993
+ }, [emblaApi, onButtonClick]);
994
+ const onNextButtonClick = useCallback(() => {
995
+ if (!emblaApi) {
996
+ return;
997
+ }
998
+ emblaApi.scrollNext();
999
+ }, [emblaApi, onButtonClick]);
1000
+ const onSelect = useCallback((api) => {
1001
+ setPrevBtnDisabled(!api.canScrollPrev());
1002
+ setNextBtnDisabled(!api.canScrollNext());
1003
+ }, []);
1004
+ useEffect(() => {
1005
+ if (!emblaApi) {
1006
+ return;
1007
+ }
1008
+ onSelect(emblaApi);
1009
+ emblaApi.on("reInit", onSelect).on("select", onSelect);
1010
+ return () => {
1011
+ emblaApi.off("reInit", onSelect).off("select", onSelect);
1012
+ };
1013
+ }, [emblaApi, onSelect]);
1014
+ return {
1015
+ prevBtnDisabled,
1016
+ nextBtnDisabled,
1017
+ onPrevButtonClick,
1018
+ onNextButtonClick
1019
+ };
1020
+ };
1021
+ var useSelectedSnapDisplay = (emblaApi) => {
1022
+ const [selectedSnap, setSelectedSnap] = useState(0);
1023
+ const [snapCount, setSnapCount] = useState(0);
1024
+ const updateScrollSnapState = useCallback((api) => {
1025
+ if (api) {
1026
+ setSnapCount(api.scrollSnapList().length);
1027
+ setSelectedSnap(api.selectedScrollSnap());
1028
+ }
1029
+ }, []);
1030
+ useEffect(() => {
1031
+ if (!emblaApi) {
1032
+ return;
1033
+ }
1034
+ updateScrollSnapState(emblaApi);
1035
+ emblaApi.on("select", updateScrollSnapState);
1036
+ emblaApi.on("reInit", updateScrollSnapState);
1037
+ return () => {
1038
+ emblaApi.off("select", updateScrollSnapState);
1039
+ emblaApi.off("reInit", updateScrollSnapState);
1040
+ };
1041
+ }, [emblaApi, updateScrollSnapState]);
1042
+ return {
1043
+ selectedSnap,
1044
+ snapCount
1045
+ };
1046
+ };
1047
+ var TipsType = ({ type }) => {
1048
+ const { t } = useTranslation();
1049
+ const { label, className } = useMemo(() => {
1050
+ const map = {
1051
+ [AnnouncementType.Listing]: {
1052
+ label: t("announcement.type.listing"),
1053
+ className: "oui-bg-primary/15 oui-text-primary"
1054
+ },
1055
+ [AnnouncementType.Maintenance]: {
1056
+ label: t("announcement.type.maintenance"),
1057
+ className: "oui-bg-[rgba(232,136,0,0.15)] oui-text-warning-darken"
1058
+ },
1059
+ [AnnouncementType.Delisting]: {
1060
+ label: t("announcement.type.delisting"),
1061
+ className: "oui-bg-[rgba(232,136,0,0.15)] oui-text-warning-darken"
1062
+ }
1063
+ };
1064
+ return map[type] || {
1065
+ label: type,
1066
+ className: map[AnnouncementType.Listing].className
1067
+ };
1068
+ }, [type, t]);
1069
+ if (!label) {
1070
+ return null;
1071
+ }
1072
+ return /* @__PURE__ */ jsx(
1073
+ Flex,
1074
+ {
1075
+ justify: "center",
1076
+ px: 2,
1077
+ r: "base",
1078
+ className: cn(
1079
+ "oui-text-2xs oui-font-medium oui-leading-[18px]",
1080
+ "oui-whitespace-nowrap oui-break-normal",
1081
+ className
1082
+ ),
1083
+ children: label
1084
+ }
1085
+ );
1086
+ };
1087
+ var AnnouncementItem2 = (props) => {
1088
+ const { type, text, url, isActive, onItemFinish, onItemClick } = props;
1089
+ const { containerRef, contentRef, overflow } = useMarqueeOnce({
1090
+ isActive,
1091
+ pxPerSec: 90,
1092
+ startDelayMs: 1e3,
1093
+ endDelayMs: 1e3,
1094
+ fallbackStayMs: 2500,
1095
+ onFinish: onItemFinish
1096
+ });
1097
+ const onClick = () => {
1098
+ if (url) {
1099
+ onItemClick?.(url);
1100
+ }
1101
+ };
1102
+ return /* @__PURE__ */ jsx(
1103
+ Flex,
1104
+ {
1105
+ height: "100%",
1106
+ itemAlign: "center",
1107
+ className: "oui-flex-none oui-basis-full oui-transform-gpu",
1108
+ children: /* @__PURE__ */ jsx(
1109
+ "div",
1110
+ {
1111
+ ref: containerRef,
1112
+ className: cn(
1113
+ "oui-relative oui-flex oui-h-[34px] oui-w-full oui-transform-gpu oui-items-center oui-overflow-hidden",
1114
+ overflow ? "oui-justify-start" : "oui-justify-center"
1115
+ ),
1116
+ children: /* @__PURE__ */ jsxs(
1117
+ "div",
1118
+ {
1119
+ ref: contentRef,
1120
+ className: cn(
1121
+ "oui-inline-flex oui-items-center oui-gap-2",
1122
+ "oui-h-[34px] oui-whitespace-nowrap oui-leading-[34px]",
1123
+ "oui-w-fit oui-transform-gpu oui-will-change-transform"
1124
+ ),
1125
+ children: [
1126
+ /* @__PURE__ */ jsx(TipsType, { type }),
1127
+ /* @__PURE__ */ jsx(
1128
+ Text,
1129
+ {
1130
+ size: "xs",
1131
+ intensity: 80,
1132
+ className: cn(
1133
+ "oui-transform-gpu",
1134
+ url ? "oui-cursor-pointer" : void 0
1135
+ ),
1136
+ onClick: url ? onClick : void 0,
1137
+ children: text
1138
+ }
1139
+ )
1140
+ ]
1141
+ }
1142
+ )
1143
+ }
1144
+ )
1145
+ }
1146
+ );
1147
+ };
1148
+ var SoundIcon = React2.forwardRef((props, ref) => {
1149
+ return /* @__PURE__ */ jsx(
1150
+ "svg",
1151
+ {
1152
+ xmlns: "http://www.w3.org/2000/svg",
1153
+ width: 18,
1154
+ height: 18,
1155
+ viewBox: "0 0 18 18",
1156
+ fill: "currentColor",
1157
+ ref,
1158
+ focusable: false,
1159
+ ...props,
1160
+ children: /* @__PURE__ */ jsx(
1161
+ "path",
1162
+ {
1163
+ d: "M11.5312 2.25534C11.2703 2.14734 10.9472 2.18333 10.7109 2.41958C10.1862 2.94383 9.52305 3.37884 8.7642 3.70434C7.99343 4.03434 6.411 4.46108 5.25547 4.45358C3.00765 4.43858 1.49993 5.71283 1.5 8.18033C1.5 10.3426 2.72745 11.6003 4.49423 11.8831L4.5 13.4476C4.5 14.6888 5.50733 15.6953 6.75 15.6953C7.89188 15.6953 8.83245 14.8118 8.97765 13.7071C8.98462 13.6538 9 13.4476 9 12.6976C9.67373 13.0118 10.232 13.5091 10.7109 13.9876C11.1834 14.4593 12 14.1166 12 13.4491C12 12.7711 11.9964 11.5433 11.9964 10.3186C12.8805 9.99159 13.5 9.15383 13.5 8.20358C13.5 7.25333 12.9149 6.39385 12.004 6.0751C12.004 4.85035 12 3.63609 12 2.95809C12 2.62434 11.7923 2.36409 11.5312 2.25534ZM13.3359 3.59033C13.1501 3.63833 12.9956 3.7636 12.8906 3.94135C12.6809 4.29835 12.7913 4.76259 13.1484 4.97184C14.2882 5.64084 15 6.85883 15 8.20358C15 9.54908 14.289 10.7663 13.1484 11.4353C12.7912 11.6446 12.6576 12.1081 12.8672 12.4651C13.0768 12.8221 13.5411 12.9323 13.8984 12.7231C15.4934 11.7878 16.4999 10.0861 16.5 8.20358C16.5 6.32183 15.4922 4.61933 13.8984 3.68408C13.7198 3.57908 13.5217 3.54233 13.3359 3.59033ZM10.5035 4.47384C10.5035 5.16759 10.5 5.89059 10.5 6.70509C10.5 8.20359 10.5 8.2036 10.5 9.7021C10.5 10.5166 10.5024 11.2103 10.5024 11.9041C8.98343 11.0078 7.41735 10.6103 6.00157 10.4828C6.00157 9.33909 5.99775 7.07709 5.99775 5.89884C6.14475 5.89209 6.31575 5.87335 6.5625 5.83885C7.52017 5.7016 8.4717 5.45259 9.375 5.06559C9.80505 4.88184 10.1247 4.71534 10.5035 4.47384ZM4.49887 6.02484C4.49887 7.17684 4.49775 9.21234 4.49775 10.3643C3.52177 10.0943 3 9.40508 3 8.18033C3 6.96983 3.47025 6.25209 4.49887 6.02484ZM6 11.9971C6.23745 12.0053 7.0977 12.1583 7.49243 12.2581L7.5 13.4476C7.5 13.8616 7.16423 14.1968 6.75 14.1968C6.33577 14.1968 6 13.8616 6 13.4476V11.9971Z",
1164
+ fill: "#fff",
1165
+ fillOpacity: 0.98
1166
+ }
1167
+ )
1168
+ }
1169
+ );
1170
+ });
1171
+ if (process.env.NODE_ENV !== "production") {
1172
+ SoundIcon.displayName = "SoundIcon";
1173
+ }
1174
+ var SwitchTips = (props) => {
1175
+ const { selectedSnap, snapCount, prevDisabled, nextDisabled, prevTips, nextTips } = props;
1176
+ const { isMobile } = useScreen();
1177
+ if (isMobile) {
1178
+ return /* @__PURE__ */ jsxs("div", { "aria-live": "polite", className: "oui-text-sm oui-tabular-nums oui-text-base-contrast-54", children: [
1179
+ selectedSnap + 1,
1180
+ "/",
1181
+ snapCount
1182
+ ] });
1183
+ }
1184
+ return /* @__PURE__ */ jsxs("div", { className: "oui-flex oui-items-center oui-justify-center oui-gap-0 oui-text-base-contrast-54", children: [
1185
+ /* @__PURE__ */ jsx(
1186
+ ChevronLeftIcon,
1187
+ {
1188
+ size: 16,
1189
+ opacity: 1,
1190
+ className: cn(
1191
+ "oui-size-4 oui-shrink-0 oui-text-base-contrast-54 hover:oui-text-base-contrast-80 lg:oui-size-5",
1192
+ prevDisabled ? "oui-cursor-not-allowed" : "oui-cursor-pointer"
1193
+ ),
1194
+ onClick: prevDisabled ? void 0 : prevTips
1195
+ }
1196
+ ),
1197
+ /* @__PURE__ */ jsxs("div", { "aria-live": "polite", className: "oui-text-sm oui-tabular-nums oui-text-base-contrast-54", children: [
1198
+ selectedSnap + 1,
1199
+ "/",
1200
+ snapCount
1201
+ ] }),
1202
+ /* @__PURE__ */ jsx(
1203
+ ChevronRightIcon,
1204
+ {
1205
+ size: 16,
1206
+ opacity: 1,
1207
+ className: cn(
1208
+ "oui-size-4 oui-shrink-0 oui-text-base-contrast-54 hover:oui-text-base-contrast-80 lg:oui-size-5",
1209
+ nextDisabled ? "oui-cursor-not-allowed" : "oui-cursor-pointer"
1210
+ ),
1211
+ onClick: nextDisabled ? void 0 : nextTips
1212
+ }
1213
+ )
1214
+ ] });
1215
+ };
1216
+ var Controls = (props) => {
1217
+ const { selectedSnap, snapCount, prevDisabled, nextDisabled, prevTips, nextTips, closeTips } = props;
1218
+ const { isMobile } = useScreen();
1219
+ return /* @__PURE__ */ jsxs(Flex, { gap: isMobile ? 1 : 2, justify: "center", itemAlign: "center", children: [
1220
+ /* @__PURE__ */ jsx(
1221
+ SwitchTips,
1222
+ {
1223
+ prevDisabled,
1224
+ nextDisabled,
1225
+ selectedSnap,
1226
+ snapCount,
1227
+ prevTips,
1228
+ nextTips
1229
+ }
1230
+ ),
1231
+ /* @__PURE__ */ jsx(
1232
+ CloseIcon,
1233
+ {
1234
+ size: 18,
1235
+ onClick: closeTips,
1236
+ className: "oui-cursor-pointer oui-text-base-contrast-80 hover:oui-text-base-contrast"
1237
+ }
1238
+ )
1239
+ ] });
1240
+ };
1241
+ var AnnouncementBannerUI = (props) => {
1242
+ const { maintenanceDialogInfo, showAnnouncement, dataSource, onClose, style, className } = props;
1243
+ const { t, i18n } = useTranslation();
1244
+ const [emblaRef, emblaApi] = useEmblaCarousel({
1245
+ loop: true,
1246
+ axis: "y",
1247
+ align: "center",
1248
+ duration: 30
1249
+ });
1250
+ const { prevBtnDisabled, nextBtnDisabled, onPrevButtonClick, onNextButtonClick } = usePrevNextButtons(emblaApi);
1251
+ const { selectedSnap, snapCount } = useSelectedSnapDisplay(emblaApi);
1252
+ const goNext = useCallback(() => {
1253
+ if (!emblaApi) {
1254
+ return;
1255
+ }
1256
+ emblaApi.scrollNext();
1257
+ }, [emblaApi]);
1258
+ if (maintenanceDialogInfo) {
1259
+ return /* @__PURE__ */ jsx(Dialog, { open: true, children: /* @__PURE__ */ jsxs(
1260
+ DialogContent,
1261
+ {
1262
+ closable: false,
1263
+ onOpenAutoFocus: (event) => event.preventDefault(),
1264
+ className: "oui-w-[320px] lg:oui-w-auto",
1265
+ children: [
1266
+ /* @__PURE__ */ jsx(DialogHeader, { children: /* @__PURE__ */ jsx(DialogTitle, { children: t("maintenance.dialog.title") }) }),
1267
+ /* @__PURE__ */ jsx(DialogBody, { className: "oui-text-2xs lg:oui-text-xs", children: maintenanceDialogInfo })
1268
+ ]
1269
+ }
1270
+ ) });
1271
+ }
1272
+ if (!showAnnouncement || !dataSource?.length) {
1273
+ return null;
1274
+ }
1275
+ return /* @__PURE__ */ jsxs(
1276
+ "div",
1277
+ {
1278
+ style,
1279
+ className: cn(
1280
+ "oui-relative oui-z-[1] oui-flex oui-transform-gpu oui-flex-row oui-flex-nowrap oui-items-center oui-justify-between oui-gap-x-1.5 oui-overflow-hidden oui-rounded-xl oui-bg-base-9 oui-px-4 oui-font-semibold",
1281
+ className
1282
+ ),
1283
+ children: [
1284
+ /* @__PURE__ */ jsx("div", { className: "oui-size-[18px]", children: /* @__PURE__ */ jsx(SoundIcon, {}) }),
1285
+ /* @__PURE__ */ jsx(
1286
+ "div",
1287
+ {
1288
+ ref: emblaRef,
1289
+ className: "oui-relative oui-h-[34px] oui-w-full oui-max-w-full oui-transform-gpu oui-overflow-hidden",
1290
+ children: /* @__PURE__ */ jsx("div", { className: "oui-flex oui-h-full oui-transform-gpu oui-flex-col", children: dataSource.map((item, index) => /* @__PURE__ */ jsx(
1291
+ AnnouncementItem2,
1292
+ {
1293
+ type: item?.type,
1294
+ text: item?.i18n?.[i18n.language] || item?.message?.trim() || "",
1295
+ url: item?.url || void 0,
1296
+ isActive: index === selectedSnap,
1297
+ onItemFinish: goNext,
1298
+ onItemClick: (url) => {
1299
+ if (props.onItemClick) {
1300
+ props.onItemClick(url);
1301
+ } else {
1302
+ window.open(url, "_blank");
1303
+ }
1304
+ }
1305
+ },
1306
+ `item-${item.announcement_id}-${index}`
1307
+ )) })
1308
+ }
1309
+ ),
1310
+ /* @__PURE__ */ jsx(
1311
+ Controls,
1312
+ {
1313
+ selectedSnap,
1314
+ snapCount,
1315
+ closeTips: onClose,
1316
+ prevTips: onPrevButtonClick,
1317
+ nextTips: onNextButtonClick,
1318
+ prevDisabled: prevBtnDisabled,
1319
+ nextDisabled: nextBtnDisabled
1320
+ }
1321
+ )
1322
+ ]
1323
+ }
1324
+ );
1325
+ };
870
1326
 
871
- export { AnnouncementCenterPage, AnnouncementCenterUI, AnnouncementItem, NotificationUI, useAnnouncement };
1327
+ export { AnnouncementBannerUI, AnnouncementCenterPage, AnnouncementCenterUI, AnnouncementItem, NotificationUI, useAnnouncement };
872
1328
  //# sourceMappingURL=out.js.map
873
1329
  //# sourceMappingURL=index.mjs.map