@kkcompany/app-ui 0.1.25 → 0.1.27

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
@@ -1,7 +1,340 @@
1
- let __emotion_react = require("@emotion/react");
2
1
  let react = require("react");
2
+ let react_jsx_runtime = require("react/jsx-runtime");
3
3
  let __emotion_react_jsx_runtime = require("@emotion/react/jsx-runtime");
4
+ let __emotion_react = require("@emotion/react");
5
+
6
+ //#region src/Banner/helpers.tsx
7
+ const getStyle = (position, offset, length) => {
8
+ return { transform: `translate3d(${(length > 2 ? -200 : 0) - position * 100}%, 0, 0) translate3d(${offset || 0}px, 0, 0)` };
9
+ };
10
+ const getIndex = (position, length) => {
11
+ if (length === 0) return 0;
12
+ return (position % length + length) % length;
13
+ };
14
+ const getItems = (children) => {
15
+ const arr = react.Children.toArray(children);
16
+ const len = arr.length;
17
+ return (len > 2 ? [
18
+ arr[len - 2],
19
+ arr[len - 1],
20
+ ...arr,
21
+ arr[0],
22
+ arr[1]
23
+ ] : arr).map((item, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
24
+ className: "banner-item",
25
+ children: item
26
+ }, `kks-banner_${index}`));
27
+ };
28
+
29
+ //#endregion
30
+ //#region src/Banner/reducer.ts
31
+ const initialState = {
32
+ position: 0,
33
+ animating: false,
34
+ dragOffset: 0
35
+ };
36
+ const reducer = (state, action) => {
37
+ switch (action.type) {
38
+ case "JUMP_TO":
39
+ if (state.animating) return state;
40
+ return {
41
+ position: action.payload.position,
42
+ animating: true,
43
+ dragOffset: 0
44
+ };
45
+ case "SETTLE": {
46
+ const { itemsLength } = action.payload;
47
+ return {
48
+ position: getIndex(state.position, itemsLength),
49
+ animating: false,
50
+ dragOffset: 0
51
+ };
52
+ }
53
+ case "DRAG": return {
54
+ ...state,
55
+ dragOffset: action.payload.dragOffset
56
+ };
57
+ default: return state;
58
+ }
59
+ };
60
+
61
+ //#endregion
62
+ //#region src/Banner/styles.ts
63
+ const iconSizeMd = "50px";
64
+ const iconSizeXl = "60px";
65
+ const iconOffset = "20%";
66
+ const dotSize = "8px";
67
+ const arrowLeftIcon = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij4gICAgPGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj4gICAgICAgIDxwYXRoIGQ9Ik0wIDBoMjR2MjRIMHoiIG9wYWNpdHk9Ii41Ii8+ICAgICAgICA8cGF0aCBmaWxsPSIjNjY2IiBmaWxsLXJ1bGU9Im5vbnplcm8iIGQ9Ik0xNS40MDkgMy44NThhMS4yNSAxLjI1IDAgMCAwLTEuODE4LTEuNzE2bC04LjUgOWExLjI1IDEuMjUgMCAwIDAgMCAxLjcxNmw4LjUgOWExLjI1IDEuMjUgMCAxIDAgMS44MTgtMS43MTZMNy43MTkgMTJsNy42OS04LjE0MnoiLz4gICAgPC9nPjwvc3ZnPg==";
68
+ const arrowRightIcon = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij4gICAgPGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj4gICAgICAgIDxwYXRoIGQ9Ik0yNCAwSDB2MjRoMjR6IiBvcGFjaXR5PSIuNSIvPiAgICAgICAgPHBhdGggZmlsbD0iIzY2NiIgZmlsbC1ydWxlPSJub256ZXJvIiBkPSJNOC41OTEgMy44NThhMS4yNSAxLjI1IDAgMCAxIDEuODE4LTEuNzE2bDguNSA5YTEuMjUgMS4yNSAwIDAgMSAwIDEuNzE2bC04LjUgOWExLjI1IDEuMjUgMCAxIDEtMS44MTgtMS43MTZMMTYuMjgxIDEyIDguNTkgMy44NTh6Ii8+ICAgIDwvZz48L3N2Zz4=";
69
+ const arrowLeftIconActive = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij4gICAgPGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj4gICAgICAgIDxwYXRoIGQ9Ik0wIDBoMjR2MjRIMHoiIG9wYWNpdHk9Ii41Ii8+ICAgICAgICA8cGF0aCBmaWxsPSIjMDBBMUIzIiBmaWxsLXJ1bGU9Im5vbnplcm8iIGQ9Ik0xNS40MDkgMy44NThhMS4yNSAxLjI1IDAgMCAwLTEuODE4LTEuNzE2bC04LjUgOWExLjI1IDEuMjUgMCAwIDAgMCAxLjcxNmw4LjUgOWExLjI1IDEuMjUgMCAxIDAgMS44MTgtMS43MTZMNy43MTkgMTJsNy42OS04LjE0MnoiLz4gICAgPC9nPjwvc3ZnPg==";
70
+ const arrowRightIconActive = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij4gICAgPGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj4gICAgICAgIDxwYXRoIGQ9Ik0yNCAwSDB2MjRoMjR6IiBvcGFjaXR5PSIuNSIvPiAgICAgICAgPHBhdGggZmlsbD0iIzAwQTFCMyIgZmlsbC1ydWxlPSJub256ZXJvIiBkPSJNOC41OTEgMy44NThhMS4yNSAxLjI1IDAgMCAxIDEuODE4LTEuNzE2bDguNSA5YTEuMjUgMS4yNSAwIDAgMSAwIDEuNzE2bC04LjUgOWExLjI1IDEuMjUgMCAxIDEtMS44MTgtMS43MTZMMTYuMjgxIDEyIDguNTkgMy44NTh6Ii8+ICAgIDwvZz48L3N2Zz4=";
71
+ const bannerStyle = {
72
+ position: "relative",
73
+ width: "100%",
74
+ whiteSpace: "nowrap",
75
+ overflow: "hidden",
76
+ userSelect: "none",
77
+ ".slider-anim": { transition: "transform 0.35s ease-out" },
78
+ ".icon-overlay": { display: "none" },
79
+ ".banner-item": {
80
+ display: "inline-block",
81
+ width: "100%"
82
+ },
83
+ ".indicator": {
84
+ position: "absolute",
85
+ left: "50%",
86
+ bottom: "5%",
87
+ transform: "translateX(-50%)",
88
+ textAlign: "center",
89
+ button: {
90
+ display: "inline-block",
91
+ margin: "0 5px",
92
+ height: dotSize,
93
+ width: dotSize,
94
+ border: 0,
95
+ padding: 0,
96
+ borderRadius: "4px",
97
+ backgroundColor: "#fff",
98
+ opacity: .5,
99
+ appearance: "none",
100
+ outline: "none",
101
+ cursor: "pointer"
102
+ },
103
+ "button.selected": {
104
+ width: "20px",
105
+ opacity: 1
106
+ }
107
+ },
108
+ "@media (min-width: 768px)": {
109
+ ".slider-mask": { padding: "0 18%" },
110
+ ".icon-overlay": {
111
+ position: "absolute",
112
+ top: 0,
113
+ bottom: 0,
114
+ display: "flex",
115
+ alignItems: "center",
116
+ width: "17%",
117
+ zIndex: 30,
118
+ "&.left": {
119
+ left: 0,
120
+ justifyContent: "flex-start",
121
+ backgroundImage: "linear-gradient(to left, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.5))"
122
+ },
123
+ "&.right": {
124
+ right: 0,
125
+ justifyContent: "flex-end",
126
+ backgroundImage: "linear-gradient(to right, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.5))"
127
+ },
128
+ "&.left .icon": {
129
+ marginLeft: iconOffset,
130
+ background: `url('${arrowLeftIcon}') no-repeat center`
131
+ },
132
+ "&.right .icon": {
133
+ marginRight: iconOffset,
134
+ background: `url('${arrowRightIcon}') no-repeat center`
135
+ },
136
+ "&.left .icon, &.right .icon": {
137
+ width: iconSizeMd,
138
+ height: iconSizeMd,
139
+ border: 0,
140
+ padding: 0,
141
+ borderRadius: "50%",
142
+ backgroundColor: "#fff",
143
+ appearance: "none",
144
+ outline: "none",
145
+ cursor: "pointer"
146
+ },
147
+ "&.left .icon:hover, &.left .icon:focus": { backgroundImage: `url('${arrowLeftIconActive}')` },
148
+ "&.right .icon:hover, &.right .icon:focus": { backgroundImage: `url('${arrowRightIconActive}')` }
149
+ },
150
+ ".banner-item": {
151
+ padding: "0 4px",
152
+ boxSizing: "border-box"
153
+ },
154
+ ".indicator button": { "&:hover, &:focus": { opacity: 1 } }
155
+ },
156
+ "@media (min-width: 1600px)": { ".icon-overlay": { "&.left .icon, &.right .icon": {
157
+ width: iconSizeXl,
158
+ height: iconSizeXl
159
+ } } }
160
+ };
161
+
162
+ //#endregion
163
+ //#region src/Banner/useAutoplay.ts
164
+ const useAutoplay = ({ enabled, interval, onTick }) => {
165
+ const onTickRef = (0, react.useRef)(onTick);
166
+ const [paused, setPaused] = (0, react.useState)(false);
167
+ const [resetCounter, setResetCounter] = (0, react.useState)(0);
168
+ (0, react.useEffect)(() => {
169
+ onTickRef.current = onTick;
170
+ }, [onTick]);
171
+ (0, react.useEffect)(() => {
172
+ if (!enabled || paused) return void 0;
173
+ const id = setInterval(() => onTickRef.current(), interval);
174
+ return () => clearInterval(id);
175
+ }, [
176
+ enabled,
177
+ interval,
178
+ paused,
179
+ resetCounter
180
+ ]);
181
+ return {
182
+ pause: (0, react.useCallback)(() => setPaused(true), []),
183
+ resume: (0, react.useCallback)(() => setPaused(false), []),
184
+ reset: (0, react.useCallback)(() => {
185
+ setPaused(false);
186
+ setResetCounter((n) => n + 1);
187
+ }, [])
188
+ };
189
+ };
4
190
 
191
+ //#endregion
192
+ //#region src/Banner/index.tsx
193
+ /** @jsxImportSource @emotion/react */
194
+ const getEventPageX = (evt) => {
195
+ const { pageX, targetTouches } = evt;
196
+ return pageX !== void 0 ? pageX : targetTouches[0].pageX;
197
+ };
198
+ const BannerRender = ({ children, className = "", autoplay = false, autoplayInterval = 5e3, pauseOnHover = false }, ref) => {
199
+ const items = (0, react.useMemo)(() => getItems(children), [children]);
200
+ const itemsLength = (0, react.useMemo)(() => react.Children.count(children), [children]);
201
+ const [state, dispatch] = (0, react.useReducer)(reducer, initialState);
202
+ const rootRef = (0, react.useRef)(null);
203
+ const dragRef = (0, react.useRef)({
204
+ dragging: false,
205
+ dragXFrom: -1,
206
+ dragXTo: -1
207
+ });
208
+ (0, react.useImperativeHandle)(ref, () => rootRef.current, []);
209
+ const indicator = getIndex(state.position, itemsLength);
210
+ const currentPosition = state.animating ? state.position : indicator;
211
+ const animStyle = getStyle(currentPosition, state.dragOffset, itemsLength);
212
+ const enabled = autoplay && itemsLength >= 2;
213
+ const hasPrev = itemsLength > 2 || currentPosition - 1 >= 0;
214
+ const hasNext = itemsLength > 2 || currentPosition + 1 < itemsLength;
215
+ const jumpTo = (0, react.useCallback)((position) => dispatch({
216
+ type: "JUMP_TO",
217
+ payload: {
218
+ position,
219
+ itemsLength
220
+ }
221
+ }), [itemsLength]);
222
+ const { pause, resume, reset } = useAutoplay({
223
+ enabled,
224
+ interval: autoplayInterval,
225
+ onTick: (0, react.useCallback)(() => {
226
+ jumpTo(hasNext ? currentPosition + 1 : currentPosition - 1);
227
+ }, [
228
+ hasNext,
229
+ currentPosition,
230
+ jumpTo
231
+ ])
232
+ });
233
+ const handleArrowBtnClick = (nextPos) => {
234
+ pause();
235
+ jumpTo(nextPos);
236
+ };
237
+ const handleIndicatorClick = (targetPos) => () => {
238
+ if (targetPos !== currentPosition) {
239
+ pause();
240
+ jumpTo(targetPos);
241
+ return;
242
+ }
243
+ reset();
244
+ };
245
+ const handleTouchStart = (evt) => {
246
+ pause();
247
+ const pageX = getEventPageX(evt);
248
+ dragRef.current = {
249
+ dragging: true,
250
+ dragXFrom: pageX,
251
+ dragXTo: pageX
252
+ };
253
+ };
254
+ const handleTouchMove = (evt) => {
255
+ if (!dragRef.current.dragging) return;
256
+ const pageX = getEventPageX(evt);
257
+ dragRef.current.dragXTo = pageX;
258
+ dispatch({
259
+ type: "DRAG",
260
+ payload: { dragOffset: pageX - dragRef.current.dragXFrom }
261
+ });
262
+ };
263
+ const handleTouchEnd = (evt) => {
264
+ const { dragXFrom, dragXTo } = dragRef.current;
265
+ const triggerWidth = evt.currentTarget.clientWidth / 4;
266
+ const movingVector = dragXTo - dragXFrom;
267
+ const movingWidth = Math.abs(movingVector);
268
+ if (movingVector > 0 && movingWidth > triggerWidth && hasPrev) jumpTo(currentPosition - 1);
269
+ else if (movingVector < 0 && movingWidth > triggerWidth && hasNext) jumpTo(currentPosition + 1);
270
+ else if (movingVector !== 0) jumpTo(currentPosition);
271
+ else resume();
272
+ dragRef.current = {
273
+ dragging: false,
274
+ dragXFrom: -1,
275
+ dragXTo: -1
276
+ };
277
+ };
278
+ const handleTransitionEnd = (evt) => {
279
+ if (evt.target !== evt.currentTarget || evt.propertyName !== "transform") return;
280
+ dispatch({
281
+ type: "SETTLE",
282
+ payload: { itemsLength }
283
+ });
284
+ resume();
285
+ };
286
+ if (itemsLength === 0) return null;
287
+ return /* @__PURE__ */ (0, __emotion_react_jsx_runtime.jsxs)("div", {
288
+ ref: rootRef,
289
+ css: bannerStyle,
290
+ className: `kks-banner ${className}`,
291
+ onMouseEnter: enabled && pauseOnHover ? pause : void 0,
292
+ onMouseLeave: enabled && pauseOnHover ? resume : void 0,
293
+ onTouchStart: handleTouchStart,
294
+ onTouchMove: handleTouchMove,
295
+ onTouchEnd: handleTouchEnd,
296
+ children: [
297
+ hasPrev && /* @__PURE__ */ (0, __emotion_react_jsx_runtime.jsx)("div", {
298
+ className: "left icon-overlay",
299
+ children: /* @__PURE__ */ (0, __emotion_react_jsx_runtime.jsx)("button", {
300
+ type: "button",
301
+ className: "icon",
302
+ onClick: () => handleArrowBtnClick(currentPosition - 1)
303
+ })
304
+ }),
305
+ /* @__PURE__ */ (0, __emotion_react_jsx_runtime.jsx)("div", {
306
+ className: "slider-mask",
307
+ children: /* @__PURE__ */ (0, __emotion_react_jsx_runtime.jsx)("div", {
308
+ className: state.animating ? "slider-anim" : "",
309
+ style: animStyle,
310
+ onTransitionEnd: handleTransitionEnd,
311
+ children: items
312
+ })
313
+ }),
314
+ hasNext && /* @__PURE__ */ (0, __emotion_react_jsx_runtime.jsx)("div", {
315
+ className: "right icon-overlay",
316
+ children: /* @__PURE__ */ (0, __emotion_react_jsx_runtime.jsx)("button", {
317
+ type: "button",
318
+ className: "icon",
319
+ onClick: () => handleArrowBtnClick(currentPosition + 1)
320
+ })
321
+ }),
322
+ /* @__PURE__ */ (0, __emotion_react_jsx_runtime.jsx)("div", {
323
+ className: "indicator",
324
+ children: Array.from({ length: itemsLength }, (_, i) => /* @__PURE__ */ (0, __emotion_react_jsx_runtime.jsx)("button", {
325
+ type: "button",
326
+ className: i === indicator ? "selected" : "",
327
+ onClick: handleIndicatorClick(i)
328
+ }, `kks-banner_indicator_${i}`))
329
+ })
330
+ ]
331
+ });
332
+ };
333
+ const Banner = (0, react.forwardRef)(BannerRender);
334
+ Banner.displayName = "Banner";
335
+ var Banner_default = Banner;
336
+
337
+ //#endregion
5
338
  //#region src/Dropdown.tsx
6
339
  /** @jsxImportSource @emotion/react */
7
340
  const dropdownStyle = __emotion_react.css`
@@ -450,6 +783,16 @@ const onResize = (container, handler) => {
450
783
  observer.observe(container);
451
784
  return () => observer.disconnect();
452
785
  };
786
+ const getLineCount = (el) => {
787
+ const style = window.getComputedStyle(el);
788
+ let lineHeight = Number.parseFloat(style.lineHeight);
789
+ if (Number.isNaN(lineHeight) || lineHeight <= 0) {
790
+ const fontSize = Number.parseFloat(style.fontSize);
791
+ lineHeight = fontSize > 0 ? fontSize * 1.2 : 0;
792
+ }
793
+ if (!lineHeight) return 0;
794
+ return Math.max(1, Math.round(el.scrollHeight / lineHeight));
795
+ };
453
796
  const defaultMessages = {
454
797
  seeMore: "もっと見る",
455
798
  seeLess: "閉じる"
@@ -457,15 +800,16 @@ const defaultMessages = {
457
800
  const SeeMore = ({ style, messages = defaultMessages, children, ...rest }) => {
458
801
  const [status, setStatus] = (0, react.useState)("no-expand");
459
802
  const [expandedHeight, setExpandedHeight] = (0, react.useState)();
803
+ const rootRef = (0, react.useRef)(null);
460
804
  const containeRef = (0, react.useRef)();
461
805
  (0, react.useEffect)(() => {
462
- if (!containeRef.current) return;
463
- return onResize(containeRef.current, () => {
806
+ if (!rootRef.current) return;
807
+ return onResize(rootRef.current, () => {
464
808
  if (!containeRef.current) return;
465
- const { scrollHeight, clientHeight } = containeRef.current;
466
- const fontSize = Number.parseFloat(window.getComputedStyle(containeRef.current).fontSize);
467
- const scrollable = scrollHeight > clientHeight * 1.3;
468
- const fit = fontSize * 3 > scrollHeight;
809
+ const collapsedLines = Number.parseInt(window.getComputedStyle(containeRef.current).getPropertyValue("--see-more-collapsed-lines"), 10) || 2;
810
+ const lineCount = getLineCount(containeRef.current);
811
+ const scrollable = lineCount > collapsedLines;
812
+ const fit = lineCount <= collapsedLines;
469
813
  setStatus((current) => current === "no-expand" && scrollable ? "collapsed" : current !== "no-expand" && fit ? "no-expand" : current);
470
814
  setExpandedHeight(containeRef.current?.scrollHeight);
471
815
  });
@@ -473,6 +817,7 @@ const SeeMore = ({ style, messages = defaultMessages, children, ...rest }) => {
473
817
  const toggle = () => setStatus((current) => current === "collapsed" ? "expanded" : "collapsing");
474
818
  return /* @__PURE__ */ (0, __emotion_react_jsx_runtime.jsxs)("div", {
475
819
  css: [containerStyle, { div: addonStyles[status] }],
820
+ ref: rootRef,
476
821
  ...rest,
477
822
  children: [/* @__PURE__ */ (0, __emotion_react_jsx_runtime.jsxs)("div", {
478
823
  css: [expandedHeight && { "--see-more-max-height": `${expandedHeight}px` }, style],
@@ -726,6 +1071,7 @@ const VideoItem = ({ style, href = "", thumbnailHref = href, imageUrl = "", data
726
1071
  var VideoItem_default = VideoItem;
727
1072
 
728
1073
  //#endregion
1074
+ exports.Banner = Banner_default;
729
1075
  exports.Dropdown = Dropdown_default;
730
1076
  exports.HiddenToggle = HiddenToggle_default;
731
1077
  exports.Icon = Icon_default;
package/dist/index.d.cts CHANGED
@@ -1,8 +1,19 @@
1
+ import * as react0 from "react";
1
2
  import { ElementType, MouseEventHandler, ReactElement, ReactNode } from "react";
2
- import * as _emotion_react_jsx_runtime0 from "@emotion/react/jsx-runtime";
3
+ import * as _emotion_react_jsx_runtime1 from "@emotion/react/jsx-runtime";
3
4
  import { CSSObject } from "@emotion/react";
4
5
  import Link from "next/link";
5
6
 
7
+ //#region src/Banner/index.d.ts
8
+ type BannerProps = {
9
+ children: ReactNode;
10
+ className?: string;
11
+ autoplay?: boolean;
12
+ autoplayInterval?: number;
13
+ pauseOnHover?: boolean;
14
+ };
15
+ declare const Banner: react0.ForwardRefExoticComponent<BannerProps & react0.RefAttributes<HTMLDivElement>>;
16
+ //#endregion
6
17
  //#region src/Dropdown.d.ts
7
18
  type DropdownOption = {
8
19
  id?: string | number;
@@ -54,7 +65,7 @@ declare const HiddenToggle: ({
54
65
  defaultEnabled?: boolean;
55
66
  children: any;
56
67
  onChange: any;
57
- }) => _emotion_react_jsx_runtime0.JSX.Element;
68
+ }) => _emotion_react_jsx_runtime1.JSX.Element;
58
69
  //#endregion
59
70
  //#region src/Icon.d.ts
60
71
  declare const Icon: ({
@@ -63,7 +74,7 @@ declare const Icon: ({
63
74
  }: {
64
75
  style?: {};
65
76
  src: any;
66
- }) => _emotion_react_jsx_runtime0.JSX.Element;
77
+ }) => _emotion_react_jsx_runtime1.JSX.Element;
67
78
  //#endregion
68
79
  //#region src/SeeMore.d.ts
69
80
  declare const defaultMessages: {
@@ -79,7 +90,7 @@ declare const SeeMore: ({
79
90
  style?: CSSObject;
80
91
  messages?: typeof defaultMessages;
81
92
  children?: ReactNode;
82
- }) => _emotion_react_jsx_runtime0.JSX.Element;
93
+ }) => _emotion_react_jsx_runtime1.JSX.Element;
83
94
  //#endregion
84
95
  //#region src/VideoItem.d.ts
85
96
  type VideoDetail = {
@@ -143,6 +154,6 @@ declare const VideoThumbnail: ({
143
154
  Image?: ElementType;
144
155
  };
145
156
  onClick?: MouseEventHandler<HTMLAnchorElement>;
146
- }) => _emotion_react_jsx_runtime0.JSX.Element;
157
+ }) => _emotion_react_jsx_runtime1.JSX.Element;
147
158
  //#endregion
148
- export { Dropdown, HiddenToggle, Icon, SeeMore, VideoItem, VideoThumbnail };
159
+ export { Banner, Dropdown, HiddenToggle, Icon, SeeMore, VideoItem, VideoThumbnail };
package/dist/index.d.mts CHANGED
@@ -1,8 +1,19 @@
1
- import { CSSObject } from "@emotion/react";
1
+ import * as react0 from "react";
2
2
  import { ElementType, MouseEventHandler, ReactElement, ReactNode } from "react";
3
3
  import * as _emotion_react_jsx_runtime1 from "@emotion/react/jsx-runtime";
4
+ import { CSSObject } from "@emotion/react";
4
5
  import Link from "next/link";
5
6
 
7
+ //#region src/Banner/index.d.ts
8
+ type BannerProps = {
9
+ children: ReactNode;
10
+ className?: string;
11
+ autoplay?: boolean;
12
+ autoplayInterval?: number;
13
+ pauseOnHover?: boolean;
14
+ };
15
+ declare const Banner: react0.ForwardRefExoticComponent<BannerProps & react0.RefAttributes<HTMLDivElement>>;
16
+ //#endregion
6
17
  //#region src/Dropdown.d.ts
7
18
  type DropdownOption = {
8
19
  id?: string | number;
@@ -145,4 +156,4 @@ declare const VideoThumbnail: ({
145
156
  onClick?: MouseEventHandler<HTMLAnchorElement>;
146
157
  }) => _emotion_react_jsx_runtime1.JSX.Element;
147
158
  //#endregion
148
- export { Dropdown, HiddenToggle, Icon, SeeMore, VideoItem, VideoThumbnail };
159
+ export { Banner, Dropdown, HiddenToggle, Icon, SeeMore, VideoItem, VideoThumbnail };
package/dist/index.mjs CHANGED
@@ -1,7 +1,340 @@
1
+ import { Children, forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useReducer, useRef, useState } from "react";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import { jsx as jsx$1, jsxs } from "@emotion/react/jsx-runtime";
1
4
  import { css } from "@emotion/react";
2
- import { useEffect, useRef, useState } from "react";
3
- import { jsx, jsxs } from "@emotion/react/jsx-runtime";
4
5
 
6
+ //#region src/Banner/helpers.tsx
7
+ const getStyle = (position, offset, length) => {
8
+ return { transform: `translate3d(${(length > 2 ? -200 : 0) - position * 100}%, 0, 0) translate3d(${offset || 0}px, 0, 0)` };
9
+ };
10
+ const getIndex = (position, length) => {
11
+ if (length === 0) return 0;
12
+ return (position % length + length) % length;
13
+ };
14
+ const getItems = (children) => {
15
+ const arr = Children.toArray(children);
16
+ const len = arr.length;
17
+ return (len > 2 ? [
18
+ arr[len - 2],
19
+ arr[len - 1],
20
+ ...arr,
21
+ arr[0],
22
+ arr[1]
23
+ ] : arr).map((item, index) => /* @__PURE__ */ jsx("div", {
24
+ className: "banner-item",
25
+ children: item
26
+ }, `kks-banner_${index}`));
27
+ };
28
+
29
+ //#endregion
30
+ //#region src/Banner/reducer.ts
31
+ const initialState = {
32
+ position: 0,
33
+ animating: false,
34
+ dragOffset: 0
35
+ };
36
+ const reducer = (state, action) => {
37
+ switch (action.type) {
38
+ case "JUMP_TO":
39
+ if (state.animating) return state;
40
+ return {
41
+ position: action.payload.position,
42
+ animating: true,
43
+ dragOffset: 0
44
+ };
45
+ case "SETTLE": {
46
+ const { itemsLength } = action.payload;
47
+ return {
48
+ position: getIndex(state.position, itemsLength),
49
+ animating: false,
50
+ dragOffset: 0
51
+ };
52
+ }
53
+ case "DRAG": return {
54
+ ...state,
55
+ dragOffset: action.payload.dragOffset
56
+ };
57
+ default: return state;
58
+ }
59
+ };
60
+
61
+ //#endregion
62
+ //#region src/Banner/styles.ts
63
+ const iconSizeMd = "50px";
64
+ const iconSizeXl = "60px";
65
+ const iconOffset = "20%";
66
+ const dotSize = "8px";
67
+ const arrowLeftIcon = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij4gICAgPGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj4gICAgICAgIDxwYXRoIGQ9Ik0wIDBoMjR2MjRIMHoiIG9wYWNpdHk9Ii41Ii8+ICAgICAgICA8cGF0aCBmaWxsPSIjNjY2IiBmaWxsLXJ1bGU9Im5vbnplcm8iIGQ9Ik0xNS40MDkgMy44NThhMS4yNSAxLjI1IDAgMCAwLTEuODE4LTEuNzE2bC04LjUgOWExLjI1IDEuMjUgMCAwIDAgMCAxLjcxNmw4LjUgOWExLjI1IDEuMjUgMCAxIDAgMS44MTgtMS43MTZMNy43MTkgMTJsNy42OS04LjE0MnoiLz4gICAgPC9nPjwvc3ZnPg==";
68
+ const arrowRightIcon = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij4gICAgPGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj4gICAgICAgIDxwYXRoIGQ9Ik0yNCAwSDB2MjRoMjR6IiBvcGFjaXR5PSIuNSIvPiAgICAgICAgPHBhdGggZmlsbD0iIzY2NiIgZmlsbC1ydWxlPSJub256ZXJvIiBkPSJNOC41OTEgMy44NThhMS4yNSAxLjI1IDAgMCAxIDEuODE4LTEuNzE2bDguNSA5YTEuMjUgMS4yNSAwIDAgMSAwIDEuNzE2bC04LjUgOWExLjI1IDEuMjUgMCAxIDEtMS44MTgtMS43MTZMMTYuMjgxIDEyIDguNTkgMy44NTh6Ii8+ICAgIDwvZz48L3N2Zz4=";
69
+ const arrowLeftIconActive = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij4gICAgPGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj4gICAgICAgIDxwYXRoIGQ9Ik0wIDBoMjR2MjRIMHoiIG9wYWNpdHk9Ii41Ii8+ICAgICAgICA8cGF0aCBmaWxsPSIjMDBBMUIzIiBmaWxsLXJ1bGU9Im5vbnplcm8iIGQ9Ik0xNS40MDkgMy44NThhMS4yNSAxLjI1IDAgMCAwLTEuODE4LTEuNzE2bC04LjUgOWExLjI1IDEuMjUgMCAwIDAgMCAxLjcxNmw4LjUgOWExLjI1IDEuMjUgMCAxIDAgMS44MTgtMS43MTZMNy43MTkgMTJsNy42OS04LjE0MnoiLz4gICAgPC9nPjwvc3ZnPg==";
70
+ const arrowRightIconActive = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij4gICAgPGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj4gICAgICAgIDxwYXRoIGQ9Ik0yNCAwSDB2MjRoMjR6IiBvcGFjaXR5PSIuNSIvPiAgICAgICAgPHBhdGggZmlsbD0iIzAwQTFCMyIgZmlsbC1ydWxlPSJub256ZXJvIiBkPSJNOC41OTEgMy44NThhMS4yNSAxLjI1IDAgMCAxIDEuODE4LTEuNzE2bDguNSA5YTEuMjUgMS4yNSAwIDAgMSAwIDEuNzE2bC04LjUgOWExLjI1IDEuMjUgMCAxIDEtMS44MTgtMS43MTZMMTYuMjgxIDEyIDguNTkgMy44NTh6Ii8+ICAgIDwvZz48L3N2Zz4=";
71
+ const bannerStyle = {
72
+ position: "relative",
73
+ width: "100%",
74
+ whiteSpace: "nowrap",
75
+ overflow: "hidden",
76
+ userSelect: "none",
77
+ ".slider-anim": { transition: "transform 0.35s ease-out" },
78
+ ".icon-overlay": { display: "none" },
79
+ ".banner-item": {
80
+ display: "inline-block",
81
+ width: "100%"
82
+ },
83
+ ".indicator": {
84
+ position: "absolute",
85
+ left: "50%",
86
+ bottom: "5%",
87
+ transform: "translateX(-50%)",
88
+ textAlign: "center",
89
+ button: {
90
+ display: "inline-block",
91
+ margin: "0 5px",
92
+ height: dotSize,
93
+ width: dotSize,
94
+ border: 0,
95
+ padding: 0,
96
+ borderRadius: "4px",
97
+ backgroundColor: "#fff",
98
+ opacity: .5,
99
+ appearance: "none",
100
+ outline: "none",
101
+ cursor: "pointer"
102
+ },
103
+ "button.selected": {
104
+ width: "20px",
105
+ opacity: 1
106
+ }
107
+ },
108
+ "@media (min-width: 768px)": {
109
+ ".slider-mask": { padding: "0 18%" },
110
+ ".icon-overlay": {
111
+ position: "absolute",
112
+ top: 0,
113
+ bottom: 0,
114
+ display: "flex",
115
+ alignItems: "center",
116
+ width: "17%",
117
+ zIndex: 30,
118
+ "&.left": {
119
+ left: 0,
120
+ justifyContent: "flex-start",
121
+ backgroundImage: "linear-gradient(to left, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.5))"
122
+ },
123
+ "&.right": {
124
+ right: 0,
125
+ justifyContent: "flex-end",
126
+ backgroundImage: "linear-gradient(to right, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.5))"
127
+ },
128
+ "&.left .icon": {
129
+ marginLeft: iconOffset,
130
+ background: `url('${arrowLeftIcon}') no-repeat center`
131
+ },
132
+ "&.right .icon": {
133
+ marginRight: iconOffset,
134
+ background: `url('${arrowRightIcon}') no-repeat center`
135
+ },
136
+ "&.left .icon, &.right .icon": {
137
+ width: iconSizeMd,
138
+ height: iconSizeMd,
139
+ border: 0,
140
+ padding: 0,
141
+ borderRadius: "50%",
142
+ backgroundColor: "#fff",
143
+ appearance: "none",
144
+ outline: "none",
145
+ cursor: "pointer"
146
+ },
147
+ "&.left .icon:hover, &.left .icon:focus": { backgroundImage: `url('${arrowLeftIconActive}')` },
148
+ "&.right .icon:hover, &.right .icon:focus": { backgroundImage: `url('${arrowRightIconActive}')` }
149
+ },
150
+ ".banner-item": {
151
+ padding: "0 4px",
152
+ boxSizing: "border-box"
153
+ },
154
+ ".indicator button": { "&:hover, &:focus": { opacity: 1 } }
155
+ },
156
+ "@media (min-width: 1600px)": { ".icon-overlay": { "&.left .icon, &.right .icon": {
157
+ width: iconSizeXl,
158
+ height: iconSizeXl
159
+ } } }
160
+ };
161
+
162
+ //#endregion
163
+ //#region src/Banner/useAutoplay.ts
164
+ const useAutoplay = ({ enabled, interval, onTick }) => {
165
+ const onTickRef = useRef(onTick);
166
+ const [paused, setPaused] = useState(false);
167
+ const [resetCounter, setResetCounter] = useState(0);
168
+ useEffect(() => {
169
+ onTickRef.current = onTick;
170
+ }, [onTick]);
171
+ useEffect(() => {
172
+ if (!enabled || paused) return void 0;
173
+ const id = setInterval(() => onTickRef.current(), interval);
174
+ return () => clearInterval(id);
175
+ }, [
176
+ enabled,
177
+ interval,
178
+ paused,
179
+ resetCounter
180
+ ]);
181
+ return {
182
+ pause: useCallback(() => setPaused(true), []),
183
+ resume: useCallback(() => setPaused(false), []),
184
+ reset: useCallback(() => {
185
+ setPaused(false);
186
+ setResetCounter((n) => n + 1);
187
+ }, [])
188
+ };
189
+ };
190
+
191
+ //#endregion
192
+ //#region src/Banner/index.tsx
193
+ /** @jsxImportSource @emotion/react */
194
+ const getEventPageX = (evt) => {
195
+ const { pageX, targetTouches } = evt;
196
+ return pageX !== void 0 ? pageX : targetTouches[0].pageX;
197
+ };
198
+ const BannerRender = ({ children, className = "", autoplay = false, autoplayInterval = 5e3, pauseOnHover = false }, ref) => {
199
+ const items = useMemo(() => getItems(children), [children]);
200
+ const itemsLength = useMemo(() => Children.count(children), [children]);
201
+ const [state, dispatch] = useReducer(reducer, initialState);
202
+ const rootRef = useRef(null);
203
+ const dragRef = useRef({
204
+ dragging: false,
205
+ dragXFrom: -1,
206
+ dragXTo: -1
207
+ });
208
+ useImperativeHandle(ref, () => rootRef.current, []);
209
+ const indicator = getIndex(state.position, itemsLength);
210
+ const currentPosition = state.animating ? state.position : indicator;
211
+ const animStyle = getStyle(currentPosition, state.dragOffset, itemsLength);
212
+ const enabled = autoplay && itemsLength >= 2;
213
+ const hasPrev = itemsLength > 2 || currentPosition - 1 >= 0;
214
+ const hasNext = itemsLength > 2 || currentPosition + 1 < itemsLength;
215
+ const jumpTo = useCallback((position) => dispatch({
216
+ type: "JUMP_TO",
217
+ payload: {
218
+ position,
219
+ itemsLength
220
+ }
221
+ }), [itemsLength]);
222
+ const { pause, resume, reset } = useAutoplay({
223
+ enabled,
224
+ interval: autoplayInterval,
225
+ onTick: useCallback(() => {
226
+ jumpTo(hasNext ? currentPosition + 1 : currentPosition - 1);
227
+ }, [
228
+ hasNext,
229
+ currentPosition,
230
+ jumpTo
231
+ ])
232
+ });
233
+ const handleArrowBtnClick = (nextPos) => {
234
+ pause();
235
+ jumpTo(nextPos);
236
+ };
237
+ const handleIndicatorClick = (targetPos) => () => {
238
+ if (targetPos !== currentPosition) {
239
+ pause();
240
+ jumpTo(targetPos);
241
+ return;
242
+ }
243
+ reset();
244
+ };
245
+ const handleTouchStart = (evt) => {
246
+ pause();
247
+ const pageX = getEventPageX(evt);
248
+ dragRef.current = {
249
+ dragging: true,
250
+ dragXFrom: pageX,
251
+ dragXTo: pageX
252
+ };
253
+ };
254
+ const handleTouchMove = (evt) => {
255
+ if (!dragRef.current.dragging) return;
256
+ const pageX = getEventPageX(evt);
257
+ dragRef.current.dragXTo = pageX;
258
+ dispatch({
259
+ type: "DRAG",
260
+ payload: { dragOffset: pageX - dragRef.current.dragXFrom }
261
+ });
262
+ };
263
+ const handleTouchEnd = (evt) => {
264
+ const { dragXFrom, dragXTo } = dragRef.current;
265
+ const triggerWidth = evt.currentTarget.clientWidth / 4;
266
+ const movingVector = dragXTo - dragXFrom;
267
+ const movingWidth = Math.abs(movingVector);
268
+ if (movingVector > 0 && movingWidth > triggerWidth && hasPrev) jumpTo(currentPosition - 1);
269
+ else if (movingVector < 0 && movingWidth > triggerWidth && hasNext) jumpTo(currentPosition + 1);
270
+ else if (movingVector !== 0) jumpTo(currentPosition);
271
+ else resume();
272
+ dragRef.current = {
273
+ dragging: false,
274
+ dragXFrom: -1,
275
+ dragXTo: -1
276
+ };
277
+ };
278
+ const handleTransitionEnd = (evt) => {
279
+ if (evt.target !== evt.currentTarget || evt.propertyName !== "transform") return;
280
+ dispatch({
281
+ type: "SETTLE",
282
+ payload: { itemsLength }
283
+ });
284
+ resume();
285
+ };
286
+ if (itemsLength === 0) return null;
287
+ return /* @__PURE__ */ jsxs("div", {
288
+ ref: rootRef,
289
+ css: bannerStyle,
290
+ className: `kks-banner ${className}`,
291
+ onMouseEnter: enabled && pauseOnHover ? pause : void 0,
292
+ onMouseLeave: enabled && pauseOnHover ? resume : void 0,
293
+ onTouchStart: handleTouchStart,
294
+ onTouchMove: handleTouchMove,
295
+ onTouchEnd: handleTouchEnd,
296
+ children: [
297
+ hasPrev && /* @__PURE__ */ jsx$1("div", {
298
+ className: "left icon-overlay",
299
+ children: /* @__PURE__ */ jsx$1("button", {
300
+ type: "button",
301
+ className: "icon",
302
+ onClick: () => handleArrowBtnClick(currentPosition - 1)
303
+ })
304
+ }),
305
+ /* @__PURE__ */ jsx$1("div", {
306
+ className: "slider-mask",
307
+ children: /* @__PURE__ */ jsx$1("div", {
308
+ className: state.animating ? "slider-anim" : "",
309
+ style: animStyle,
310
+ onTransitionEnd: handleTransitionEnd,
311
+ children: items
312
+ })
313
+ }),
314
+ hasNext && /* @__PURE__ */ jsx$1("div", {
315
+ className: "right icon-overlay",
316
+ children: /* @__PURE__ */ jsx$1("button", {
317
+ type: "button",
318
+ className: "icon",
319
+ onClick: () => handleArrowBtnClick(currentPosition + 1)
320
+ })
321
+ }),
322
+ /* @__PURE__ */ jsx$1("div", {
323
+ className: "indicator",
324
+ children: Array.from({ length: itemsLength }, (_, i) => /* @__PURE__ */ jsx$1("button", {
325
+ type: "button",
326
+ className: i === indicator ? "selected" : "",
327
+ onClick: handleIndicatorClick(i)
328
+ }, `kks-banner_indicator_${i}`))
329
+ })
330
+ ]
331
+ });
332
+ };
333
+ const Banner = forwardRef(BannerRender);
334
+ Banner.displayName = "Banner";
335
+ var Banner_default = Banner;
336
+
337
+ //#endregion
5
338
  //#region src/Dropdown.tsx
6
339
  /** @jsxImportSource @emotion/react */
7
340
  const dropdownStyle = css`
@@ -196,10 +529,10 @@ const Button = ({ showMenu, label, placeholder, disabled, onClick }) => /* @__PU
196
529
  "aria-haspopup": "true",
197
530
  "aria-expanded": showMenu,
198
531
  disabled,
199
- children: [/* @__PURE__ */ jsx("span", {
532
+ children: [/* @__PURE__ */ jsx$1("span", {
200
533
  className: `font text-truncate${!label ? " kks-dropdown__label--grey-off" : ""}`,
201
534
  children: !label ? placeholder : label
202
- }), /* @__PURE__ */ jsx("span", { className: "icon kks-dropdown__arrow-icon" })]
535
+ }), /* @__PURE__ */ jsx$1("span", { className: "icon kks-dropdown__arrow-icon" })]
203
536
  });
204
537
  const Menu = ({ menuTitle, items, activeIndex = -1, onClickItem, onClickClose }) => {
205
538
  const menuRef = useRef(null);
@@ -209,7 +542,7 @@ const Menu = ({ menuTitle, items, activeIndex = -1, onClickItem, onClickClose })
209
542
  const handleOverlayClick = (e) => {
210
543
  if (e.target.id === "kks-menu-overlay") onClickClose();
211
544
  };
212
- return /* @__PURE__ */ jsx("div", {
545
+ return /* @__PURE__ */ jsx$1("div", {
213
546
  id: "kks-menu-overlay",
214
547
  className: "kks-dropdown__menu-overlay",
215
548
  onClick: handleOverlayClick,
@@ -220,33 +553,33 @@ const Menu = ({ menuTitle, items, activeIndex = -1, onClickItem, onClickClose })
220
553
  className: "kks-dropdown__menu",
221
554
  children: [/* @__PURE__ */ jsxs("div", {
222
555
  className: "item-row kks-dropdown__menu-header",
223
- children: [menuTitle !== "" && /* @__PURE__ */ jsx("div", {
556
+ children: [menuTitle !== "" && /* @__PURE__ */ jsx$1("div", {
224
557
  className: "font text-truncate kks-dropdown__menu-title",
225
558
  children: menuTitle
226
- }), /* @__PURE__ */ jsx("div", {
559
+ }), /* @__PURE__ */ jsx$1("div", {
227
560
  className: "kks-dropdown__close-btn",
228
561
  onClick: onClickClose,
229
562
  onKeyPress: onClickClose,
230
563
  role: "button",
231
564
  tabIndex: 0,
232
- children: /* @__PURE__ */ jsx("span", { className: "icon kks-dropdown__close-icon" })
565
+ children: /* @__PURE__ */ jsx$1("span", { className: "icon kks-dropdown__close-icon" })
233
566
  })]
234
- }), /* @__PURE__ */ jsx("ul", {
567
+ }), /* @__PURE__ */ jsx$1("ul", {
235
568
  className: "kks-dropdown__menu-body",
236
569
  children: items.map((item, index) => {
237
570
  const { id, label, route, value } = item;
238
571
  const isActive = activeIndex === index;
239
- return /* @__PURE__ */ jsx("li", {
572
+ return /* @__PURE__ */ jsx$1("li", {
240
573
  className: isActive ? "kks-dropdown__menu-item--active" : "",
241
574
  children: /* @__PURE__ */ jsxs("a", {
242
575
  className: "item-row kks-dropdown__menu-link",
243
576
  href: route || "#",
244
577
  onClick: onClickItem(item, index),
245
578
  "data-value": value,
246
- children: [/* @__PURE__ */ jsx("span", {
579
+ children: [/* @__PURE__ */ jsx$1("span", {
247
580
  className: "font text-truncate",
248
581
  children: label || value
249
- }), isActive && /* @__PURE__ */ jsx("span", { className: "icon kks-dropdown__check-icon" })]
582
+ }), isActive && /* @__PURE__ */ jsx$1("span", { className: "icon kks-dropdown__check-icon" })]
250
583
  })
251
584
  }, `item_${id || value || index}`);
252
585
  })
@@ -287,13 +620,13 @@ const Dropdown = ({ className, options = [], value = null, activeIndex = -1, pla
287
620
  css: dropdownStyle,
288
621
  className: `kks-dropdown ${className || ""}`,
289
622
  ref: dropdownRef,
290
- children: [/* @__PURE__ */ jsx(Button, {
623
+ children: [/* @__PURE__ */ jsx$1(Button, {
291
624
  showMenu,
292
625
  label: selectedLabel,
293
626
  placeholder,
294
627
  disabled,
295
628
  onClick: toggleMenu
296
- }), showMenu && /* @__PURE__ */ jsx(Menu, {
629
+ }), showMenu && /* @__PURE__ */ jsx$1(Menu, {
297
630
  menuTitle,
298
631
  items: options,
299
632
  activeIndex: selectedIndex,
@@ -339,11 +672,11 @@ const HiddenToggle = ({ defaultEnabled = false, children, onChange, ...rest }) =
339
672
  style: { opacity: tapCount / 7 },
340
673
  children: [7 - tapCount, " more tap(s) for feature toggle"]
341
674
  }),
342
- tapCount >= 7 && /* @__PURE__ */ jsx("span", {
675
+ tapCount >= 7 && /* @__PURE__ */ jsx$1("span", {
343
676
  css: statusStyle,
344
677
  children: "feature toggle"
345
678
  }),
346
- tapCount >= 7 && /* @__PURE__ */ jsx("button", {
679
+ tapCount >= 7 && /* @__PURE__ */ jsx$1("button", {
347
680
  type: "button",
348
681
  onClick: onOptOut,
349
682
  children: "exit"
@@ -367,7 +700,7 @@ const iconStyle = {
367
700
  pointerEvents: "none",
368
701
  touchAction: "none"
369
702
  };
370
- const Icon = ({ style = {}, src }) => /* @__PURE__ */ jsx("span", { css: [
703
+ const Icon = ({ style = {}, src }) => /* @__PURE__ */ jsx$1("span", { css: [
371
704
  iconStyle,
372
705
  { maskImage: `url(data:image/svg+xml;base64,${src})` },
373
706
  style
@@ -450,6 +783,16 @@ const onResize = (container, handler) => {
450
783
  observer.observe(container);
451
784
  return () => observer.disconnect();
452
785
  };
786
+ const getLineCount = (el) => {
787
+ const style = window.getComputedStyle(el);
788
+ let lineHeight = Number.parseFloat(style.lineHeight);
789
+ if (Number.isNaN(lineHeight) || lineHeight <= 0) {
790
+ const fontSize = Number.parseFloat(style.fontSize);
791
+ lineHeight = fontSize > 0 ? fontSize * 1.2 : 0;
792
+ }
793
+ if (!lineHeight) return 0;
794
+ return Math.max(1, Math.round(el.scrollHeight / lineHeight));
795
+ };
453
796
  const defaultMessages = {
454
797
  seeMore: "もっと見る",
455
798
  seeLess: "閉じる"
@@ -457,15 +800,16 @@ const defaultMessages = {
457
800
  const SeeMore = ({ style, messages = defaultMessages, children, ...rest }) => {
458
801
  const [status, setStatus] = useState("no-expand");
459
802
  const [expandedHeight, setExpandedHeight] = useState();
803
+ const rootRef = useRef(null);
460
804
  const containeRef = useRef();
461
805
  useEffect(() => {
462
- if (!containeRef.current) return;
463
- return onResize(containeRef.current, () => {
806
+ if (!rootRef.current) return;
807
+ return onResize(rootRef.current, () => {
464
808
  if (!containeRef.current) return;
465
- const { scrollHeight, clientHeight } = containeRef.current;
466
- const fontSize = Number.parseFloat(window.getComputedStyle(containeRef.current).fontSize);
467
- const scrollable = scrollHeight > clientHeight * 1.3;
468
- const fit = fontSize * 3 > scrollHeight;
809
+ const collapsedLines = Number.parseInt(window.getComputedStyle(containeRef.current).getPropertyValue("--see-more-collapsed-lines"), 10) || 2;
810
+ const lineCount = getLineCount(containeRef.current);
811
+ const scrollable = lineCount > collapsedLines;
812
+ const fit = lineCount <= collapsedLines;
469
813
  setStatus((current) => current === "no-expand" && scrollable ? "collapsed" : current !== "no-expand" && fit ? "no-expand" : current);
470
814
  setExpandedHeight(containeRef.current?.scrollHeight);
471
815
  });
@@ -473,17 +817,18 @@ const SeeMore = ({ style, messages = defaultMessages, children, ...rest }) => {
473
817
  const toggle = () => setStatus((current) => current === "collapsed" ? "expanded" : "collapsing");
474
818
  return /* @__PURE__ */ jsxs("div", {
475
819
  css: [containerStyle, { div: addonStyles[status] }],
820
+ ref: rootRef,
476
821
  ...rest,
477
822
  children: [/* @__PURE__ */ jsxs("div", {
478
823
  css: [expandedHeight && { "--see-more-max-height": `${expandedHeight}px` }, style],
479
824
  ref: containeRef,
480
825
  onTransitionEnd: () => setStatus((current) => current === "collapsing" ? "collapsed" : current),
481
- children: [children, /* @__PURE__ */ jsx("div", { css: maskStyle })]
826
+ children: [children, /* @__PURE__ */ jsx$1("div", { css: maskStyle })]
482
827
  }), /* @__PURE__ */ jsxs("button", {
483
828
  type: "button",
484
829
  disabled: status === "no-expand",
485
830
  onClick: toggle,
486
- children: [status === "collapsed" ? messages.seeMore : messages.seeLess, /* @__PURE__ */ jsx(Icon_default, { src: see_more_default })]
831
+ children: [status === "collapsed" ? messages.seeMore : messages.seeLess, /* @__PURE__ */ jsx$1(Icon_default, { src: see_more_default })]
487
832
  })]
488
833
  });
489
834
  };
@@ -572,7 +917,7 @@ const VideoThumbnail = ({ href, imageUrl, title, progress, topTags = [], bottomT
572
917
  Image: "img",
573
918
  ...slots
574
919
  };
575
- return /* @__PURE__ */ jsx("div", {
920
+ return /* @__PURE__ */ jsx$1("div", {
576
921
  css: { position: "relative" },
577
922
  children: /* @__PURE__ */ jsxs(components.Link, {
578
923
  css: videoThumbnailStyles,
@@ -580,7 +925,7 @@ const VideoThumbnail = ({ href, imageUrl, title, progress, topTags = [], bottomT
580
925
  onClick,
581
926
  onContextMenu: (event) => event.preventDefault(),
582
927
  children: [
583
- /* @__PURE__ */ jsx(components.Image, {
928
+ /* @__PURE__ */ jsx$1(components.Image, {
584
929
  css: thumbnailImageStyles,
585
930
  src: imageUrl,
586
931
  alt: title,
@@ -588,15 +933,15 @@ const VideoThumbnail = ({ href, imageUrl, title, progress, topTags = [], bottomT
588
933
  loading: "lazy"
589
934
  }),
590
935
  children,
591
- progress >= 0 && /* @__PURE__ */ jsx("div", {
936
+ progress >= 0 && /* @__PURE__ */ jsx$1("div", {
592
937
  css: progressStyles,
593
- children: /* @__PURE__ */ jsx("span", { style: { width: `${progress * 100}%` } })
938
+ children: /* @__PURE__ */ jsx$1("span", { style: { width: `${progress * 100}%` } })
594
939
  }),
595
940
  /* @__PURE__ */ jsxs("div", {
596
941
  css: tagBaseStyles,
597
- children: [/* @__PURE__ */ jsx("div", { children: topTags?.map((tag) => /* @__PURE__ */ jsx("div", { children: tag }, tag)) }), bottomTags.length > 0 && /* @__PURE__ */ jsx("div", {
942
+ children: [/* @__PURE__ */ jsx$1("div", { children: topTags?.map((tag) => /* @__PURE__ */ jsx$1("div", { children: tag }, tag)) }), bottomTags.length > 0 && /* @__PURE__ */ jsx$1("div", {
598
943
  css: [tagBottomStyles, progress >= 0 && { marginBottom: "8px" }],
599
- children: bottomTags?.map((tag) => /* @__PURE__ */ jsx("div", { children: tag }, tag))
944
+ children: bottomTags?.map((tag) => /* @__PURE__ */ jsx$1("div", { children: tag }, tag))
600
945
  })]
601
946
  })
602
947
  ]
@@ -679,11 +1024,11 @@ const desktopDescriptionStyle = {
679
1024
  margin: "0.75rem 0",
680
1025
  WebkitLineClamp: LINES_OF_DESCRIPTION
681
1026
  };
682
- const OptionalLink = ({ slots, href, children, ...rest }) => href ? /* @__PURE__ */ jsx(slots.Link, {
1027
+ const OptionalLink = ({ slots, href, children, ...rest }) => href ? /* @__PURE__ */ jsx$1(slots.Link, {
683
1028
  href,
684
1029
  ...rest,
685
1030
  children
686
- }) : /* @__PURE__ */ jsx("a", {
1031
+ }) : /* @__PURE__ */ jsx$1("a", {
687
1032
  ...rest,
688
1033
  children
689
1034
  });
@@ -692,7 +1037,7 @@ const VideoItem = ({ style, href = "", thumbnailHref = href, imageUrl = "", data
692
1037
  css: [videoItemStyle, style],
693
1038
  children: [/* @__PURE__ */ jsxs("div", {
694
1039
  css: infoStyle,
695
- children: [/* @__PURE__ */ jsx(VideoThumbnail_default, {
1040
+ children: [/* @__PURE__ */ jsx$1(VideoThumbnail_default, {
696
1041
  href: thumbnailHref,
697
1042
  imageUrl,
698
1043
  topTags,
@@ -705,18 +1050,18 @@ const VideoItem = ({ style, href = "", thumbnailHref = href, imageUrl = "", data
705
1050
  slots,
706
1051
  href,
707
1052
  children: [
708
- /* @__PURE__ */ jsx("div", {
1053
+ /* @__PURE__ */ jsx$1("div", {
709
1054
  css: titleStyle,
710
1055
  children: data.title || data.display_text
711
1056
  }),
712
- /* @__PURE__ */ jsx("div", {
1057
+ /* @__PURE__ */ jsx$1("div", {
713
1058
  css: [descriptionStyle, desktopDescriptionStyle],
714
1059
  children: data.synopsis
715
1060
  }),
716
- secondaryText ? /* @__PURE__ */ jsx("div", { children: secondaryText }) : null
1061
+ secondaryText ? /* @__PURE__ */ jsx$1("div", { children: secondaryText }) : null
717
1062
  ]
718
1063
  })]
719
- }), /* @__PURE__ */ jsx(OptionalLink, {
1064
+ }), /* @__PURE__ */ jsx$1(OptionalLink, {
720
1065
  slots,
721
1066
  href,
722
1067
  css: descriptionStyle,
@@ -726,4 +1071,4 @@ const VideoItem = ({ style, href = "", thumbnailHref = href, imageUrl = "", data
726
1071
  var VideoItem_default = VideoItem;
727
1072
 
728
1073
  //#endregion
729
- export { Dropdown_default as Dropdown, HiddenToggle_default as HiddenToggle, Icon_default as Icon, SeeMore_default as SeeMore, VideoItem_default as VideoItem, VideoThumbnail_default as VideoThumbnail };
1074
+ 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 };
package/package.json CHANGED
@@ -1,39 +1,39 @@
1
1
  {
2
- "name": "@kkcompany/app-ui",
3
- "version": "0.1.25",
4
- "module": "dist/index.mjs",
5
- "main": "dist/index.js",
6
- "types": "dist/index.d.mts",
7
- "files": [
8
- "dist"
9
- ],
10
- "sideEffects": false,
11
- "exports": {
12
- ".": {
13
- "import": "./dist/index.mjs",
14
- "module": "./dist/index.mjs",
15
- "types": "./dist/index.d.mts",
16
- "require": "./dist/index.cjs"
17
- }
18
- },
19
- "license": "MIT",
20
- "scripts": {
21
- "dev": "next",
22
- "build": "tsdown --format esm --format cjs"
23
- },
24
- "dependencies": {
25
- "@emotion/react": "^11.14.0"
26
- },
27
- "peerDependencies": {
28
- "react": "^16.8.0",
29
- "react-dom": "^16.8.0"
30
- },
31
- "devDependencies": {
32
- "@biomejs/biome": "^2.3.5",
33
- "binary-base64-loader": "^1.0.0",
34
- "next": "^16.0.2",
35
- "rollup-plugin-base64": "^1.0.1",
36
- "tsdown": "^0.16.4",
37
- "typescript": "^5.9.3"
38
- }
2
+ "name": "@kkcompany/app-ui",
3
+ "version": "0.1.27",
4
+ "module": "dist/index.mjs",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.mts",
7
+ "files": [
8
+ "dist"
9
+ ],
10
+ "sideEffects": false,
11
+ "exports": {
12
+ ".": {
13
+ "import": "./dist/index.mjs",
14
+ "module": "./dist/index.mjs",
15
+ "types": "./dist/index.d.mts",
16
+ "require": "./dist/index.cjs"
17
+ }
18
+ },
19
+ "license": "MIT",
20
+ "scripts": {
21
+ "dev": "next",
22
+ "build": "tsdown --format esm --format cjs"
23
+ },
24
+ "dependencies": {
25
+ "@emotion/react": "^11.14.0"
26
+ },
27
+ "peerDependencies": {
28
+ "react": "^16.8.0",
29
+ "react-dom": "^16.8.0"
30
+ },
31
+ "devDependencies": {
32
+ "@biomejs/biome": "^2.3.5",
33
+ "binary-base64-loader": "^1.0.0",
34
+ "next": "^16.0.2",
35
+ "rollup-plugin-base64": "^1.0.1",
36
+ "tsdown": "^0.16.4",
37
+ "typescript": "^5.9.3"
38
+ }
39
39
  }