@cntrl-site/sdk-nextjs 1.6.12 → 1.6.13

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 (122) hide show
  1. package/.idea/codeStyles/codeStyleConfig.xml +5 -0
  2. package/.idea/modules.xml +8 -0
  3. package/.idea/sdk-nextjs.iml +12 -0
  4. package/.idea/vcs.xml +6 -0
  5. package/Icon/r +0 -0
  6. package/LICENSE +21 -21
  7. package/README.md +4 -4
  8. package/TODO.md +1 -1
  9. package/eslint.config.mjs +35 -35
  10. package/jest.config.js +6 -6
  11. package/lib/common/useItemFXData.js +7 -7
  12. package/lib/components/Article.js +5 -5
  13. package/lib/components/LayoutStyle.js +4 -4
  14. package/lib/components/Section/Section.js +78 -15
  15. package/lib/components/items/CodeEmbedItem/CodeEmbedItem.js +30 -30
  16. package/lib/components/items/CompoundItem/CompoundChild.js +20 -20
  17. package/lib/components/items/CompoundItem/CompoundItem.js +14 -14
  18. package/lib/components/items/CustomItem/CustomItem.js +9 -9
  19. package/lib/components/items/EmbedVideoItem/VimeoEmbed.js +23 -23
  20. package/lib/components/items/EmbedVideoItem/YoutubeEmbed.js +26 -26
  21. package/lib/components/items/FileItem/ImageItem.js +38 -38
  22. package/lib/components/items/FileItem/VideoItem.js +45 -45
  23. package/lib/components/items/GroupItem/GroupItem.js +13 -13
  24. package/lib/components/items/Item.js +30 -30
  25. package/lib/components/items/RectangleItem/RectangleItem.js +20 -20
  26. package/lib/components/items/RichTextItem/RichTextItem.js +21 -21
  27. package/lib/utils/RichTextConverter/RichTextConverter.js +21 -21
  28. package/package.json +57 -57
  29. package/src/common/useCurrentLayout.ts +52 -52
  30. package/src/common/useExemplary.ts +9 -9
  31. package/src/common/useItemFXData.ts +47 -47
  32. package/src/common/useKeyframeValue.ts +85 -85
  33. package/src/common/useRegisterResize.ts +17 -17
  34. package/src/components/Article.tsx +66 -66
  35. package/src/components/ArticleWrapper.tsx +21 -21
  36. package/src/components/Head.tsx +56 -56
  37. package/src/components/LayoutStyle.tsx +21 -21
  38. package/src/components/Page.tsx +33 -33
  39. package/src/components/ScrollPlaybackVideo.tsx +56 -56
  40. package/src/components/Section/Section.tsx +164 -77
  41. package/src/components/Section/useSectionColor.ts +19 -19
  42. package/src/components/Section/useSectionHeightMap.ts +19 -19
  43. package/src/components/items/CodeEmbedItem/CodeEmbedItem.tsx +132 -132
  44. package/src/components/items/CodeEmbedItem/useCodeEmbedItem.ts +36 -36
  45. package/src/components/items/CompoundItem/CompoundChild.tsx +150 -150
  46. package/src/components/items/CompoundItem/CompoundItem.tsx +71 -71
  47. package/src/components/items/CompoundItem/useCompoundItem.ts +21 -21
  48. package/src/components/items/CustomItem/CustomItem.tsx +47 -47
  49. package/src/components/items/EmbedVideoItem/VimeoEmbed.tsx +202 -202
  50. package/src/components/items/EmbedVideoItem/YoutubeEmbed.tsx +187 -187
  51. package/src/components/items/EmbedVideoItem/useEmbedVideoItem.ts +46 -46
  52. package/src/components/items/FileItem/ImageItem.tsx +156 -156
  53. package/src/components/items/FileItem/VideoItem.tsx +185 -185
  54. package/src/components/items/FileItem/useFileItem.ts +73 -73
  55. package/src/components/items/GroupItem/GroupItem.tsx +79 -79
  56. package/src/components/items/GroupItem/useGroupItem.ts +34 -34
  57. package/src/components/items/Item.tsx +251 -251
  58. package/src/components/items/LinkWrapper.tsx +39 -39
  59. package/src/components/items/RectangleItem/RectangleItem.tsx +95 -95
  60. package/src/components/items/RectangleItem/useRectangleItem.ts +82 -82
  61. package/src/components/items/RichTextItem/RichTextItem.tsx +105 -105
  62. package/src/components/items/RichTextItem/useRichTextItem.ts +12 -12
  63. package/src/components/items/RichTextWrapper.tsx +15 -15
  64. package/src/components/items/itemsMap.ts +26 -26
  65. package/src/components/items/useDraggable.ts +252 -252
  66. package/src/components/items/useItemAngle.ts +13 -13
  67. package/src/components/items/useItemArea.ts +35 -35
  68. package/src/components/items/useItemPointerEvents.ts +23 -23
  69. package/src/components/items/useItemScale.ts +17 -17
  70. package/src/components/items/useItemTriggers.ts +15 -15
  71. package/src/components/items/useRichTextItemValues.ts +65 -65
  72. package/src/components/items/useSizing.ts +25 -25
  73. package/src/components/items/useStickyItemTop.ts +21 -21
  74. package/src/components/useLayoutContext.ts +7 -7
  75. package/src/index.ts +24 -24
  76. package/src/interactions/CSSPropertyNameMap.ts +38 -38
  77. package/src/interactions/InteractionsRegistry.ts +275 -275
  78. package/src/interactions/ItemInteractionCtrl.ts +77 -77
  79. package/src/interactions/getTransition.ts +27 -27
  80. package/src/interactions/types.ts +36 -36
  81. package/src/interactions/useItemInteractionCtrl.ts +18 -18
  82. package/src/provider/ArticleRectContext.ts +4 -4
  83. package/src/provider/CntrlContext.ts +5 -5
  84. package/src/provider/CntrlProvider.tsx +11 -11
  85. package/src/provider/CntrlSdkContext.ts +69 -69
  86. package/src/provider/CustomItemRegistry.ts +14 -14
  87. package/src/provider/CustomItemTypes.ts +3 -3
  88. package/src/provider/CustomSectionRegistry.ts +25 -25
  89. package/src/provider/InteractionsContext.old.tsx +65 -65
  90. package/src/provider/InteractionsContext.test.tsx +97 -97
  91. package/src/provider/InteractionsContext.tsx +28 -28
  92. package/src/provider/Keyframes.ts +11 -11
  93. package/src/provider/KeyframesContext.ts +5 -5
  94. package/src/provider/LayoutContext.ts +3 -3
  95. package/src/provider/defaultContext.ts +8 -8
  96. package/src/provider/useCntrlContext.ts +8 -8
  97. package/src/styled-jsx.d.ts +1 -1
  98. package/src/utils/Animator/Animator.ts +371 -371
  99. package/src/utils/ArticleRectManager/ArticleRectObserver.ts +93 -93
  100. package/src/utils/ArticleRectManager/useArticleRectObserver.ts +17 -17
  101. package/src/utils/ArticleRectManager/useSectionRegistry.ts +11 -11
  102. package/src/utils/EventEmitter.ts +41 -41
  103. package/src/utils/RichTextConverter/RichTextConverter.tsx +326 -326
  104. package/src/utils/ScaleAnchorMap.ts +13 -13
  105. package/src/utils/StickyManager/StickyManager.ts +23 -23
  106. package/src/utils/Youtube/YouTubeIframeApiLoader.ts +58 -58
  107. package/src/utils/Youtube/YoutubeIframeApi.ts +101 -101
  108. package/src/utils/Youtube/useYouTubeIframeApi.ts +12 -12
  109. package/src/utils/binSearchInsertAt.ts +33 -33
  110. package/src/utils/castObject.ts +10 -10
  111. package/src/utils/effects/useImageFx.ts +113 -113
  112. package/src/utils/effects/useVideoFx.ts +116 -116
  113. package/src/utils/getAnchoredItemTop.ts +12 -12
  114. package/src/utils/getCompoundBondaryStyles.ts +61 -62
  115. package/src/utils/getFontFamilyValue.ts +3 -3
  116. package/src/utils/getInvertedRanges.ts +43 -43
  117. package/src/utils/getItemTopStyle.ts +14 -14
  118. package/src/utils/getStyleFromItemStateAndParams.ts +8 -8
  119. package/src/utils/getValidYoutubeUrl.ts +29 -29
  120. package/src/utils/isItemType.ts +6 -6
  121. package/src/utils/rangeMap.ts +13 -13
  122. 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
+ }