@cntrl-site/sdk-nextjs 1.8.15 → 1.8.17

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.
Files changed (126) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +4 -4
  3. package/TODO.md +1 -1
  4. package/cntrl-site-sdk-nextjs-1.8.12.tgz +0 -0
  5. package/cntrl-site-sdk-nextjs-1.8.13.tgz +0 -0
  6. package/cntrl-site-sdk-nextjs-1.8.14.tgz +0 -0
  7. package/eslint.config.mjs +35 -35
  8. package/jest.config.js +6 -6
  9. package/lib/common/useItemFXData.js +7 -7
  10. package/lib/components/Article.js +5 -5
  11. package/lib/components/LayoutStyle.js +4 -4
  12. package/lib/components/Section/Section.js +43 -43
  13. package/lib/components/items/CodeEmbedItem/CodeEmbedItem.js +35 -35
  14. package/lib/components/items/ComponentItem/ComponentItem.js +12 -12
  15. package/lib/components/items/CompoundItem/CompoundChild.js +20 -20
  16. package/lib/components/items/CompoundItem/CompoundItem.js +14 -14
  17. package/lib/components/items/CustomItem/CustomItem.js +9 -9
  18. package/lib/components/items/EmbedVideoItem/VimeoEmbed.js +23 -23
  19. package/lib/components/items/EmbedVideoItem/YoutubeEmbed.js +26 -26
  20. package/lib/components/items/FileItem/ImageItem.js +38 -38
  21. package/lib/components/items/FileItem/VideoItem.js +45 -45
  22. package/lib/components/items/GroupItem/GroupItem.js +13 -13
  23. package/lib/components/items/Item.js +30 -30
  24. package/lib/components/items/RectangleItem/RectangleItem.js +20 -20
  25. package/lib/components/items/RichTextItem/RichTextItem.js +21 -21
  26. package/lib/utils/RichTextConverter/RichTextConverter.js +21 -21
  27. package/lib/utils/effects/useImageFx.js +2 -2
  28. package/lib/utils/effects/useVideoFx.js +2 -2
  29. package/package.json +57 -57
  30. package/src/common/useCurrentLayout.ts +52 -52
  31. package/src/common/useExemplary.ts +9 -9
  32. package/src/common/useItemFXData.ts +47 -47
  33. package/src/common/useKeyframeValue.ts +85 -85
  34. package/src/common/useRegisterResize.ts +16 -16
  35. package/src/components/Article.tsx +73 -73
  36. package/src/components/ArticleWrapper.tsx +21 -21
  37. package/src/components/Head.tsx +56 -56
  38. package/src/components/LayoutStyle.tsx +21 -21
  39. package/src/components/Page.tsx +33 -33
  40. package/src/components/ScrollPlaybackVideo.tsx +56 -56
  41. package/src/components/Section/Section.tsx +164 -164
  42. package/src/components/Section/useSectionColor.ts +19 -19
  43. package/src/components/Section/useSectionHeightMap.ts +19 -19
  44. package/src/components/items/CodeEmbedItem/CodeEmbedItem.tsx +137 -137
  45. package/src/components/items/CodeEmbedItem/useCodeEmbedItem.ts +36 -36
  46. package/src/components/items/ComponentItem/ComponentItem.tsx +65 -65
  47. package/src/components/items/ComponentItem/useComponentItem.ts +35 -35
  48. package/src/components/items/CompoundItem/CompoundChild.tsx +150 -150
  49. package/src/components/items/CompoundItem/CompoundItem.tsx +70 -70
  50. package/src/components/items/CompoundItem/useCompoundItem.ts +21 -21
  51. package/src/components/items/CustomItem/CustomItem.tsx +48 -48
  52. package/src/components/items/EmbedVideoItem/VimeoEmbed.tsx +202 -202
  53. package/src/components/items/EmbedVideoItem/YoutubeEmbed.tsx +187 -187
  54. package/src/components/items/EmbedVideoItem/useEmbedVideoItem.ts +46 -46
  55. package/src/components/items/FileItem/ImageItem.tsx +156 -156
  56. package/src/components/items/FileItem/VideoItem.tsx +185 -185
  57. package/src/components/items/FileItem/useFileItem.ts +73 -73
  58. package/src/components/items/GroupItem/GroupItem.tsx +79 -79
  59. package/src/components/items/GroupItem/useGroupItem.ts +34 -34
  60. package/src/components/items/Item.tsx +251 -251
  61. package/src/components/items/LinkWrapper.tsx +39 -39
  62. package/src/components/items/RectangleItem/RectangleItem.tsx +95 -95
  63. package/src/components/items/RectangleItem/useRectangleItem.ts +82 -82
  64. package/src/components/items/RichTextItem/RichTextItem.tsx +105 -105
  65. package/src/components/items/RichTextItem/useRichTextItem.ts +12 -12
  66. package/src/components/items/RichTextWrapper.tsx +15 -15
  67. package/src/components/items/itemsMap.ts +28 -28
  68. package/src/components/items/useDraggable.ts +252 -252
  69. package/src/components/items/useItemAngle.ts +13 -13
  70. package/src/components/items/useItemArea.ts +35 -35
  71. package/src/components/items/useItemPointerEvents.ts +23 -23
  72. package/src/components/items/useItemScale.ts +17 -17
  73. package/src/components/items/useItemTriggers.ts +15 -15
  74. package/src/components/items/useRichTextItemValues.ts +65 -65
  75. package/src/components/items/useSizing.ts +25 -25
  76. package/src/components/items/useStickyItemTop.ts +21 -21
  77. package/src/components/useLayoutContext.ts +7 -7
  78. package/src/index.ts +24 -24
  79. package/src/interactions/CSSPropertyNameMap.ts +38 -38
  80. package/src/interactions/InteractionsRegistry.ts +283 -283
  81. package/src/interactions/ItemInteractionCtrl.ts +77 -77
  82. package/src/interactions/getTransition.ts +27 -27
  83. package/src/interactions/types.ts +36 -36
  84. package/src/interactions/useItemInteractionCtrl.ts +18 -18
  85. package/src/provider/ArticleRectContext.ts +4 -4
  86. package/src/provider/CntrlContext.ts +5 -5
  87. package/src/provider/CntrlProvider.tsx +11 -11
  88. package/src/provider/CntrlSdkContext.ts +83 -83
  89. package/src/provider/CustomItemRegistry.ts +14 -14
  90. package/src/provider/CustomItemTypes.ts +3 -3
  91. package/src/provider/CustomSectionRegistry.ts +25 -25
  92. package/src/provider/InteractionsContext.old.tsx +66 -66
  93. package/src/provider/InteractionsContext.test.tsx +97 -97
  94. package/src/provider/InteractionsContext.tsx +28 -28
  95. package/src/provider/Keyframes.ts +11 -11
  96. package/src/provider/KeyframesContext.ts +5 -5
  97. package/src/provider/LayoutContext.ts +3 -3
  98. package/src/provider/WebGLContextManagerContext.tsx +4 -4
  99. package/src/provider/defaultContext.ts +8 -8
  100. package/src/provider/useCntrlContext.ts +8 -8
  101. package/src/styled-jsx.d.ts +1 -1
  102. package/src/utils/Animator/Animator.ts +371 -371
  103. package/src/utils/ArticleRectManager/ArticleRectObserver.ts +93 -93
  104. package/src/utils/ArticleRectManager/useArticleRectObserver.ts +17 -17
  105. package/src/utils/ArticleRectManager/useSectionRegistry.ts +11 -11
  106. package/src/utils/EventEmitter.ts +41 -41
  107. package/src/utils/RichTextConverter/RichTextConverter.tsx +326 -326
  108. package/src/utils/ScaleAnchorMap.ts +13 -13
  109. package/src/utils/StickyManager/StickyManager.ts +23 -23
  110. package/src/utils/Youtube/YouTubeIframeApiLoader.ts +58 -58
  111. package/src/utils/Youtube/YoutubeIframeApi.ts +101 -101
  112. package/src/utils/Youtube/useYouTubeIframeApi.ts +12 -12
  113. package/src/utils/binSearchInsertAt.ts +33 -33
  114. package/src/utils/castObject.ts +10 -10
  115. package/src/utils/effects/useImageFx.ts +156 -156
  116. package/src/utils/effects/useVideoFx.ts +158 -158
  117. package/src/utils/getAnchoredItemTop.ts +12 -12
  118. package/src/utils/getCompoundBoundaryStyles.ts +60 -60
  119. package/src/utils/getFontFamilyValue.ts +3 -3
  120. package/src/utils/getInvertedRanges.ts +43 -43
  121. package/src/utils/getItemTopStyle.ts +14 -14
  122. package/src/utils/getStyleFromItemStateAndParams.ts +8 -8
  123. package/src/utils/getValidYoutubeUrl.ts +29 -29
  124. package/src/utils/isItemType.ts +5 -5
  125. package/src/utils/rangeMap.ts +13 -13
  126. package/src/utils/useElementRect.ts +23 -23
@@ -1,13 +1,13 @@
1
- import { AreaAnchor as ScaleAnchor } from '@cntrl-site/sdk';
2
-
3
- export const ScaleAnchorMap = {
4
- [ScaleAnchor.TopLeft]: 'top left',
5
- [ScaleAnchor.TopCenter]: 'top center',
6
- [ScaleAnchor.TopRight]: 'top right',
7
- [ScaleAnchor.MiddleLeft]: 'center left',
8
- [ScaleAnchor.MiddleCenter]: 'center',
9
- [ScaleAnchor.MiddleRight]: 'center right',
10
- [ScaleAnchor.BottomLeft]: 'bottom left',
11
- [ScaleAnchor.BottomCenter]: 'bottom center',
12
- [ScaleAnchor.BottomRight]: 'bottom right'
13
- };
1
+ import { AreaAnchor as ScaleAnchor } from '@cntrl-site/sdk';
2
+
3
+ export const ScaleAnchorMap = {
4
+ [ScaleAnchor.TopLeft]: 'top left',
5
+ [ScaleAnchor.TopCenter]: 'top center',
6
+ [ScaleAnchor.TopRight]: 'top right',
7
+ [ScaleAnchor.MiddleLeft]: 'center left',
8
+ [ScaleAnchor.MiddleCenter]: 'center',
9
+ [ScaleAnchor.MiddleRight]: 'center right',
10
+ [ScaleAnchor.BottomLeft]: 'bottom left',
11
+ [ScaleAnchor.BottomCenter]: 'bottom center',
12
+ [ScaleAnchor.BottomRight]: 'bottom right'
13
+ };
@@ -1,23 +1,23 @@
1
- export type TSticky = { from: number; to?: number; } | null;
2
-
3
- export class StickyManager {
4
- constructor(
5
- private sticky: TSticky
6
- ) {}
7
-
8
- getIsSticky(scroll: number): boolean {
9
- if (!this.sticky) return false;
10
- return (this.sticky.from <= scroll) && (!this.sticky.to || this.sticky.to >= scroll);
11
- }
12
-
13
- getPosition(scroll: number, top: number): number {
14
- if (!this.sticky) return top;
15
- if (this.getIsSticky(scroll)) {
16
- return top - this.sticky.from;
17
- }
18
- if (this.sticky.to !== undefined && this.sticky.to <= scroll) {
19
- return top + this.sticky.to - this.sticky.from;
20
- }
21
- return top;
22
- }
23
- }
1
+ export type TSticky = { from: number; to?: number; } | null;
2
+
3
+ export class StickyManager {
4
+ constructor(
5
+ private sticky: TSticky
6
+ ) {}
7
+
8
+ getIsSticky(scroll: number): boolean {
9
+ if (!this.sticky) return false;
10
+ return (this.sticky.from <= scroll) && (!this.sticky.to || this.sticky.to >= scroll);
11
+ }
12
+
13
+ getPosition(scroll: number, top: number): number {
14
+ if (!this.sticky) return top;
15
+ if (this.getIsSticky(scroll)) {
16
+ return top - this.sticky.from;
17
+ }
18
+ if (this.sticky.to !== undefined && this.sticky.to <= scroll) {
19
+ return top + this.sticky.to - this.sticky.from;
20
+ }
21
+ return top;
22
+ }
23
+ }
@@ -1,58 +1,58 @@
1
- import { YT } from './YoutubeIframeApi';
2
-
3
- const YT_IFRAME_API_URL = 'https://www.youtube.com/iframe_api';
4
-
5
- export class YouTubeIframeApiLoader {
6
- private static instance: YouTubeIframeApiLoader | undefined;
7
-
8
- public static create(): YouTubeIframeApiLoader {
9
- if (!YouTubeIframeApiLoader.instance) {
10
- YouTubeIframeApiLoader.instance = new YouTubeIframeApiLoader();
11
- }
12
- return YouTubeIframeApiLoader.instance;
13
- }
14
-
15
- private loadingPromise: Promise<void>;
16
-
17
- private constructor() {
18
- this.loadingPromise = this.loadApi();
19
- }
20
-
21
- async getApi(): Promise<YT> {
22
- await this.loadingPromise;
23
- if (!window.YT) {
24
- throw new Error('YouTube IFrame API is not loaded');
25
- }
26
- return window.YT;
27
- }
28
-
29
- private loadApi(): Promise<void> {
30
- return new Promise<void>((resolve, reject) => {
31
- const script = document.createElement('script');
32
- script.src = YT_IFRAME_API_URL;
33
- const loadError = new Error('YouTube IFrame API loading failed');
34
- captureStackTrace(loadError);
35
- script.addEventListener('abort', () => {
36
- reject(loadError);
37
- });
38
- script.addEventListener('error', () => {
39
- reject(loadError);
40
- });
41
- window.onYouTubeIframeAPIReady = () => {
42
- window.onYouTubeIframeAPIReady = undefined;
43
- resolve();
44
- };
45
- document.head.appendChild(script);
46
- });
47
- }
48
- }
49
-
50
- export function captureStackTrace(error: Error): void {
51
- if (typeof Error.captureStackTrace === 'function') {
52
- Error.captureStackTrace(error);
53
- return;
54
- }
55
- try {
56
- throw error;
57
- } catch (errWithStack) {}
58
- }
1
+ import { YT } from './YoutubeIframeApi';
2
+
3
+ const YT_IFRAME_API_URL = 'https://www.youtube.com/iframe_api';
4
+
5
+ export class YouTubeIframeApiLoader {
6
+ private static instance: YouTubeIframeApiLoader | undefined;
7
+
8
+ public static create(): YouTubeIframeApiLoader {
9
+ if (!YouTubeIframeApiLoader.instance) {
10
+ YouTubeIframeApiLoader.instance = new YouTubeIframeApiLoader();
11
+ }
12
+ return YouTubeIframeApiLoader.instance;
13
+ }
14
+
15
+ private loadingPromise: Promise<void>;
16
+
17
+ private constructor() {
18
+ this.loadingPromise = this.loadApi();
19
+ }
20
+
21
+ async getApi(): Promise<YT> {
22
+ await this.loadingPromise;
23
+ if (!window.YT) {
24
+ throw new Error('YouTube IFrame API is not loaded');
25
+ }
26
+ return window.YT;
27
+ }
28
+
29
+ private loadApi(): Promise<void> {
30
+ return new Promise<void>((resolve, reject) => {
31
+ const script = document.createElement('script');
32
+ script.src = YT_IFRAME_API_URL;
33
+ const loadError = new Error('YouTube IFrame API loading failed');
34
+ captureStackTrace(loadError);
35
+ script.addEventListener('abort', () => {
36
+ reject(loadError);
37
+ });
38
+ script.addEventListener('error', () => {
39
+ reject(loadError);
40
+ });
41
+ window.onYouTubeIframeAPIReady = () => {
42
+ window.onYouTubeIframeAPIReady = undefined;
43
+ resolve();
44
+ };
45
+ document.head.appendChild(script);
46
+ });
47
+ }
48
+ }
49
+
50
+ export function captureStackTrace(error: Error): void {
51
+ if (typeof Error.captureStackTrace === 'function') {
52
+ Error.captureStackTrace(error);
53
+ return;
54
+ }
55
+ try {
56
+ throw error;
57
+ } catch (errWithStack) {}
58
+ }
@@ -1,101 +1,101 @@
1
- export interface YT {
2
- Player: YTPlayerConstructor;
3
- }
4
-
5
- export interface YTPlayerConstructor {
6
- new(container: string | Element, params: YTPlayerParams): YTPlayer;
7
- }
8
-
9
- export interface YTEvent<Data> {
10
- target: YTPlayer;
11
- data: Data;
12
- }
13
-
14
- type YTBool = 0 | 1 | '0' | '1';
15
-
16
- export interface YTPlayerParams {
17
- width?: string | number;
18
- height?: string | number;
19
- videoId?: string;
20
- playerVars?: Partial<{
21
- autoplay: YTBool;
22
- cc_lang_pref: string;
23
- cc_load_policy: YTBool;
24
- color: string;
25
- controls: YTBool;
26
- disablekb: YTBool;
27
- enablejsapi: YTBool;
28
- end: number | string;
29
- fs: YTBool;
30
- hl: string;
31
- iv_load_policy: 1 | 3 | '1' | '3';
32
- list: string;
33
- listType: 'playlist' | 'user_uploads';
34
- loop: YTBool;
35
- origin: string;
36
- playlist: string;
37
- playsinline: YTBool;
38
- rel: YTBool;
39
- start: number | string;
40
- widget_referrer: string;
41
- }>;
42
- events?: Partial<{
43
- onReady: (event: YTEvent<void>) => void;
44
- onStateChange: (event: YTEvent<YTPlayerState>) => void;
45
- }>
46
- }
47
-
48
- export enum YTPlayerState {
49
- Unstarted = -1,
50
- Ended = 0,
51
- Playing = 1,
52
- Paused = 2,
53
- Buffering = 3,
54
- VideoCued = 5
55
- }
56
-
57
- export interface YTPlayer {
58
- loadVideoById(videoId: string, startSeconds?: number): void;
59
- loadVideoById(params: {
60
- videoId: string;
61
- startSeconds?: number;
62
- endSeconds?: number;
63
- }): void;
64
-
65
- loadVideoByUrl(mediaContentUrl: string, startSeconds?: number): void;
66
- loadVideoByUrl(params: {
67
- mediaContentUrl: string;
68
- startSeconds?: number;
69
- endSeconds?: number;
70
- }): void;
71
-
72
- playVideo(): void;
73
- pauseVideo(): void;
74
- stopVideo(): void;
75
- seekTo(seconds: number, allowSeekAhead: boolean): void;
76
-
77
- mute(): void;
78
- unMute(): void;
79
- isMuted(): void;
80
- setVolume(volume: number): void;
81
- getVolume(): void;
82
- setSize(width: number, height: number): void;
83
- setLoop(loopPlaylists: boolean): void;
84
-
85
- getVideoLoadedFraction(): number;
86
- getPlayerState(): YTPlayerState;
87
-
88
- getDuration(): number; // in seconds
89
- getVideoUrl(): string;
90
- getVideoEmbedCode(): string;
91
-
92
- getIframe(): HTMLIFrameElement | undefined;
93
- destroy(): void;
94
- }
95
-
96
- declare global {
97
- interface Window {
98
- YT?: YT;
99
- onYouTubeIframeAPIReady?: () => void;
100
- }
101
- }
1
+ export interface YT {
2
+ Player: YTPlayerConstructor;
3
+ }
4
+
5
+ export interface YTPlayerConstructor {
6
+ new(container: string | Element, params: YTPlayerParams): YTPlayer;
7
+ }
8
+
9
+ export interface YTEvent<Data> {
10
+ target: YTPlayer;
11
+ data: Data;
12
+ }
13
+
14
+ type YTBool = 0 | 1 | '0' | '1';
15
+
16
+ export interface YTPlayerParams {
17
+ width?: string | number;
18
+ height?: string | number;
19
+ videoId?: string;
20
+ playerVars?: Partial<{
21
+ autoplay: YTBool;
22
+ cc_lang_pref: string;
23
+ cc_load_policy: YTBool;
24
+ color: string;
25
+ controls: YTBool;
26
+ disablekb: YTBool;
27
+ enablejsapi: YTBool;
28
+ end: number | string;
29
+ fs: YTBool;
30
+ hl: string;
31
+ iv_load_policy: 1 | 3 | '1' | '3';
32
+ list: string;
33
+ listType: 'playlist' | 'user_uploads';
34
+ loop: YTBool;
35
+ origin: string;
36
+ playlist: string;
37
+ playsinline: YTBool;
38
+ rel: YTBool;
39
+ start: number | string;
40
+ widget_referrer: string;
41
+ }>;
42
+ events?: Partial<{
43
+ onReady: (event: YTEvent<void>) => void;
44
+ onStateChange: (event: YTEvent<YTPlayerState>) => void;
45
+ }>
46
+ }
47
+
48
+ export enum YTPlayerState {
49
+ Unstarted = -1,
50
+ Ended = 0,
51
+ Playing = 1,
52
+ Paused = 2,
53
+ Buffering = 3,
54
+ VideoCued = 5
55
+ }
56
+
57
+ export interface YTPlayer {
58
+ loadVideoById(videoId: string, startSeconds?: number): void;
59
+ loadVideoById(params: {
60
+ videoId: string;
61
+ startSeconds?: number;
62
+ endSeconds?: number;
63
+ }): void;
64
+
65
+ loadVideoByUrl(mediaContentUrl: string, startSeconds?: number): void;
66
+ loadVideoByUrl(params: {
67
+ mediaContentUrl: string;
68
+ startSeconds?: number;
69
+ endSeconds?: number;
70
+ }): void;
71
+
72
+ playVideo(): void;
73
+ pauseVideo(): void;
74
+ stopVideo(): void;
75
+ seekTo(seconds: number, allowSeekAhead: boolean): void;
76
+
77
+ mute(): void;
78
+ unMute(): void;
79
+ isMuted(): void;
80
+ setVolume(volume: number): void;
81
+ getVolume(): void;
82
+ setSize(width: number, height: number): void;
83
+ setLoop(loopPlaylists: boolean): void;
84
+
85
+ getVideoLoadedFraction(): number;
86
+ getPlayerState(): YTPlayerState;
87
+
88
+ getDuration(): number; // in seconds
89
+ getVideoUrl(): string;
90
+ getVideoEmbedCode(): string;
91
+
92
+ getIframe(): HTMLIFrameElement | undefined;
93
+ destroy(): void;
94
+ }
95
+
96
+ declare global {
97
+ interface Window {
98
+ YT?: YT;
99
+ onYouTubeIframeAPIReady?: () => void;
100
+ }
101
+ }
@@ -1,12 +1,12 @@
1
- import { useEffect, useState } from 'react';
2
- import { YT } from './YoutubeIframeApi';
3
- import { YouTubeIframeApiLoader } from './YouTubeIframeApiLoader';
4
-
5
- export function useYouTubeIframeApi(): YT | undefined {
6
- const [YT, setYT] = useState<YT | undefined>(undefined);
7
- useEffect(() => {
8
- const loader = YouTubeIframeApiLoader.create();
9
- loader.getApi().then((YT) => setYT(YT));
10
- }, []);
11
- return YT;
12
- }
1
+ import { useEffect, useState } from 'react';
2
+ import { YT } from './YoutubeIframeApi';
3
+ import { YouTubeIframeApiLoader } from './YouTubeIframeApiLoader';
4
+
5
+ export function useYouTubeIframeApi(): YT | undefined {
6
+ const [YT, setYT] = useState<YT | undefined>(undefined);
7
+ useEffect(() => {
8
+ const loader = YouTubeIframeApiLoader.create();
9
+ loader.getApi().then((YT) => setYT(YT));
10
+ }, []);
11
+ return YT;
12
+ }
@@ -1,33 +1,33 @@
1
- type CompareFn<L, R> = (lhs: L, rhs: R) => number;
2
- type InsertStrategy<T> = (list: T[], item: T, compare: CompareFn<T, T>) => number;
3
-
4
- export function binSearchInsertAt<T, K>(list: T[], item: K, compare: CompareFn<K, T>): number {
5
- switch (list.length) {
6
- case 0: return 0;
7
- case 1: return compare(item, list[0]) < 0 ? 0 : 1;
8
- }
9
- let start = 0;
10
- let end = list.length;
11
- let pivot: number;
12
- let cmpResult: number;
13
- while (start < end) {
14
- pivot = start + Math.floor((end - start) / 2);
15
- cmpResult = compare(item, list[pivot]);
16
- if (cmpResult === 0) return pivot + 1;
17
- if (cmpResult < 0) {
18
- end = pivot;
19
- }
20
- if (cmpResult > 0) {
21
- start = pivot + 1;
22
- }
23
- }
24
- if (start >= list.length) return list.length;
25
- return compare(item, list[start]) < 0 ? start : start + 1;
26
- }
27
-
28
- export function createInsert<T>(start: InsertStrategy<T>, compare: CompareFn<T, T>): (list: T[], item: T) => void {
29
- return (list: T[], item: T): void => {
30
- const pos = start(list, item, compare);
31
- list.splice(pos, 0, item);
32
- };
33
- }
1
+ type CompareFn<L, R> = (lhs: L, rhs: R) => number;
2
+ type InsertStrategy<T> = (list: T[], item: T, compare: CompareFn<T, T>) => number;
3
+
4
+ export function binSearchInsertAt<T, K>(list: T[], item: K, compare: CompareFn<K, T>): number {
5
+ switch (list.length) {
6
+ case 0: return 0;
7
+ case 1: return compare(item, list[0]) < 0 ? 0 : 1;
8
+ }
9
+ let start = 0;
10
+ let end = list.length;
11
+ let pivot: number;
12
+ let cmpResult: number;
13
+ while (start < end) {
14
+ pivot = start + Math.floor((end - start) / 2);
15
+ cmpResult = compare(item, list[pivot]);
16
+ if (cmpResult === 0) return pivot + 1;
17
+ if (cmpResult < 0) {
18
+ end = pivot;
19
+ }
20
+ if (cmpResult > 0) {
21
+ start = pivot + 1;
22
+ }
23
+ }
24
+ if (start >= list.length) return list.length;
25
+ return compare(item, list[start]) < 0 ? start : start + 1;
26
+ }
27
+
28
+ export function createInsert<T>(start: InsertStrategy<T>, compare: CompareFn<T, T>): (list: T[], item: T) => void {
29
+ return (list: T[], item: T): void => {
30
+ const pos = start(list, item, compare);
31
+ list.splice(pos, 0, item);
32
+ };
33
+ }
@@ -1,10 +1,10 @@
1
- type Ctor = {
2
- new(...args: any): any;
3
- };
4
-
5
- export function castObject<O extends Ctor>(value: unknown, ctor: O): InstanceType<O> {
6
- if (value instanceof ctor) {
7
- return value;
8
- }
9
- throw new TypeError(`passed value "${String(value)}" is not instance of ${ctor.name}`);
10
- }
1
+ type Ctor = {
2
+ new(...args: any): any;
3
+ };
4
+
5
+ export function castObject<O extends Ctor>(value: unknown, ctor: O): InstanceType<O> {
6
+ if (value instanceof ctor) {
7
+ return value;
8
+ }
9
+ throw new TypeError(`passed value "${String(value)}" is not instance of ${ctor.name}`);
10
+ }