@fgv/ts-sudoku-ui 5.0.1-2

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 (190) hide show
  1. package/.rush/temp/chunked-rush-logs/ts-sudoku-ui.build.chunks.jsonl +12 -0
  2. package/.rush/temp/operation/build/log-chunks.jsonl +12 -0
  3. package/.rush/temp/operation/build/state.json +3 -0
  4. package/.rush/temp/shrinkwrap-deps.json +1087 -0
  5. package/.test-failures-catalog.md +470 -0
  6. package/CSS_MODULES_MIGRATION_SUMMARY.md +189 -0
  7. package/dist/ts-sudoku-ui.d.ts +668 -0
  8. package/etc/ts-sudoku-ui.api.md +481 -0
  9. package/lib/components/CageOverlay.d.ts +8 -0
  10. package/lib/components/CageOverlay.d.ts.map +1 -0
  11. package/lib/components/CageOverlay.js +133 -0
  12. package/lib/components/CageOverlay.js.map +1 -0
  13. package/lib/components/CageSumIndicator.d.ts +8 -0
  14. package/lib/components/CageSumIndicator.d.ts.map +1 -0
  15. package/lib/components/CageSumIndicator.js +66 -0
  16. package/lib/components/CageSumIndicator.js.map +1 -0
  17. package/lib/components/CombinationCard.d.ts +8 -0
  18. package/lib/components/CombinationCard.d.ts.map +1 -0
  19. package/lib/components/CombinationCard.js +64 -0
  20. package/lib/components/CombinationCard.js.map +1 -0
  21. package/lib/components/CombinationGrid.d.ts +8 -0
  22. package/lib/components/CombinationGrid.d.ts.map +1 -0
  23. package/lib/components/CombinationGrid.js +51 -0
  24. package/lib/components/CombinationGrid.js.map +1 -0
  25. package/lib/components/CompactControlRibbon.d.ts +45 -0
  26. package/lib/components/CompactControlRibbon.d.ts.map +1 -0
  27. package/lib/components/CompactControlRibbon.js +141 -0
  28. package/lib/components/CompactControlRibbon.js.map +1 -0
  29. package/lib/components/DualKeypad.d.ts +39 -0
  30. package/lib/components/DualKeypad.d.ts.map +1 -0
  31. package/lib/components/DualKeypad.js +137 -0
  32. package/lib/components/DualKeypad.js.map +1 -0
  33. package/lib/components/KillerCombinationsExplorer.d.ts +9 -0
  34. package/lib/components/KillerCombinationsExplorer.d.ts.map +1 -0
  35. package/lib/components/KillerCombinationsExplorer.js +69 -0
  36. package/lib/components/KillerCombinationsExplorer.js.map +1 -0
  37. package/lib/components/KillerCombinationsModal.d.ts +20 -0
  38. package/lib/components/KillerCombinationsModal.d.ts.map +1 -0
  39. package/lib/components/KillerCombinationsModal.js +135 -0
  40. package/lib/components/KillerCombinationsModal.js.map +1 -0
  41. package/lib/components/KillerCombinationsPanel.d.ts +20 -0
  42. package/lib/components/KillerCombinationsPanel.d.ts.map +1 -0
  43. package/lib/components/KillerCombinationsPanel.js +79 -0
  44. package/lib/components/KillerCombinationsPanel.js.map +1 -0
  45. package/lib/components/NumberKeypad.d.ts +30 -0
  46. package/lib/components/NumberKeypad.d.ts.map +1 -0
  47. package/lib/components/NumberKeypad.js +181 -0
  48. package/lib/components/NumberKeypad.js.map +1 -0
  49. package/lib/components/SudokuCell.d.ts +8 -0
  50. package/lib/components/SudokuCell.d.ts.map +1 -0
  51. package/lib/components/SudokuCell.js +240 -0
  52. package/lib/components/SudokuCell.js.map +1 -0
  53. package/lib/components/SudokuControls.d.ts +8 -0
  54. package/lib/components/SudokuControls.d.ts.map +1 -0
  55. package/lib/components/SudokuControls.js +140 -0
  56. package/lib/components/SudokuControls.js.map +1 -0
  57. package/lib/components/SudokuGrid.d.ts +8 -0
  58. package/lib/components/SudokuGrid.d.ts.map +1 -0
  59. package/lib/components/SudokuGrid.js +177 -0
  60. package/lib/components/SudokuGrid.js.map +1 -0
  61. package/lib/components/SudokuGridEntry.d.ts +8 -0
  62. package/lib/components/SudokuGridEntry.d.ts.map +1 -0
  63. package/lib/components/SudokuGridEntry.js +373 -0
  64. package/lib/components/SudokuGridEntry.js.map +1 -0
  65. package/lib/components/ValidationDisplay.d.ts +8 -0
  66. package/lib/components/ValidationDisplay.d.ts.map +1 -0
  67. package/lib/components/ValidationDisplay.js +95 -0
  68. package/lib/components/ValidationDisplay.js.map +1 -0
  69. package/lib/contexts/DiagnosticLoggerContext.d.ts +81 -0
  70. package/lib/contexts/DiagnosticLoggerContext.d.ts.map +1 -0
  71. package/lib/contexts/DiagnosticLoggerContext.js +98 -0
  72. package/lib/contexts/DiagnosticLoggerContext.js.map +1 -0
  73. package/lib/contexts/index.d.ts +3 -0
  74. package/lib/contexts/index.d.ts.map +1 -0
  75. package/lib/contexts/index.js +25 -0
  76. package/lib/contexts/index.js.map +1 -0
  77. package/lib/hooks/useCombinationElimination.d.ts +10 -0
  78. package/lib/hooks/useCombinationElimination.d.ts.map +1 -0
  79. package/lib/hooks/useCombinationElimination.js +123 -0
  80. package/lib/hooks/useCombinationElimination.js.map +1 -0
  81. package/lib/hooks/useKeyboardShortcut.d.ts +19 -0
  82. package/lib/hooks/useKeyboardShortcut.d.ts.map +1 -0
  83. package/lib/hooks/useKeyboardShortcut.js +55 -0
  84. package/lib/hooks/useKeyboardShortcut.js.map +1 -0
  85. package/lib/hooks/useKillerCombinations.d.ts +11 -0
  86. package/lib/hooks/useKillerCombinations.d.ts.map +1 -0
  87. package/lib/hooks/useKillerCombinations.js +65 -0
  88. package/lib/hooks/useKillerCombinations.js.map +1 -0
  89. package/lib/hooks/useLongPress.d.ts +31 -0
  90. package/lib/hooks/useLongPress.d.ts.map +1 -0
  91. package/lib/hooks/useLongPress.js +69 -0
  92. package/lib/hooks/useLongPress.js.map +1 -0
  93. package/lib/hooks/usePuzzleSession.d.ts +32 -0
  94. package/lib/hooks/usePuzzleSession.d.ts.map +1 -0
  95. package/lib/hooks/usePuzzleSession.js +398 -0
  96. package/lib/hooks/usePuzzleSession.js.map +1 -0
  97. package/lib/hooks/useResponsiveLayout.d.ts +44 -0
  98. package/lib/hooks/useResponsiveLayout.d.ts.map +1 -0
  99. package/lib/hooks/useResponsiveLayout.js +139 -0
  100. package/lib/hooks/useResponsiveLayout.js.map +1 -0
  101. package/lib/index.d.ts +28 -0
  102. package/lib/index.d.ts.map +1 -0
  103. package/lib/index.js +49 -0
  104. package/lib/index.js.map +1 -0
  105. package/lib/test/fixtures/killerSudokuPuzzles.d.ts +36 -0
  106. package/lib/test/fixtures/killerSudokuPuzzles.d.ts.map +1 -0
  107. package/lib/test/fixtures/killerSudokuPuzzles.js +57 -0
  108. package/lib/test/fixtures/killerSudokuPuzzles.js.map +1 -0
  109. package/lib/test/unit/components/SudokuGridEntry.testHelpers.d.ts +22 -0
  110. package/lib/test/unit/components/SudokuGridEntry.testHelpers.d.ts.map +1 -0
  111. package/lib/test/unit/components/SudokuGridEntry.testHelpers.js +116 -0
  112. package/lib/test/unit/components/SudokuGridEntry.testHelpers.js.map +1 -0
  113. package/lib/tsdoc-metadata.json +11 -0
  114. package/lib/types/index.d.ts +234 -0
  115. package/lib/types/index.d.ts.map +1 -0
  116. package/lib/types/index.js +25 -0
  117. package/lib/types/index.js.map +1 -0
  118. package/lib/utils/CageLookupManager.d.ts +74 -0
  119. package/lib/utils/CageLookupManager.d.ts.map +1 -0
  120. package/lib/utils/CageLookupManager.js +283 -0
  121. package/lib/utils/CageLookupManager.js.map +1 -0
  122. package/lib/utils/CagePatternManager.d.ts +82 -0
  123. package/lib/utils/CagePatternManager.d.ts.map +1 -0
  124. package/lib/utils/CagePatternManager.js +139 -0
  125. package/lib/utils/CagePatternManager.js.map +1 -0
  126. package/lib/utils/puzzleDefinitionHelper.d.ts +25 -0
  127. package/lib/utils/puzzleDefinitionHelper.d.ts.map +1 -0
  128. package/lib/utils/puzzleDefinitionHelper.js +99 -0
  129. package/lib/utils/puzzleDefinitionHelper.js.map +1 -0
  130. package/lib-commonjs/components/CageOverlay.js +138 -0
  131. package/lib-commonjs/components/CageOverlay.js.map +1 -0
  132. package/lib-commonjs/components/CageSumIndicator.js +71 -0
  133. package/lib-commonjs/components/CageSumIndicator.js.map +1 -0
  134. package/lib-commonjs/components/CombinationCard.js +68 -0
  135. package/lib-commonjs/components/CombinationCard.js.map +1 -0
  136. package/lib-commonjs/components/CombinationGrid.js +56 -0
  137. package/lib-commonjs/components/CombinationGrid.js.map +1 -0
  138. package/lib-commonjs/components/CompactControlRibbon.js +146 -0
  139. package/lib-commonjs/components/CompactControlRibbon.js.map +1 -0
  140. package/lib-commonjs/components/DualKeypad.js +142 -0
  141. package/lib-commonjs/components/DualKeypad.js.map +1 -0
  142. package/lib-commonjs/components/KillerCombinationsExplorer.js +74 -0
  143. package/lib-commonjs/components/KillerCombinationsExplorer.js.map +1 -0
  144. package/lib-commonjs/components/KillerCombinationsModal.js +140 -0
  145. package/lib-commonjs/components/KillerCombinationsModal.js.map +1 -0
  146. package/lib-commonjs/components/KillerCombinationsPanel.js +84 -0
  147. package/lib-commonjs/components/KillerCombinationsPanel.js.map +1 -0
  148. package/lib-commonjs/components/NumberKeypad.js +186 -0
  149. package/lib-commonjs/components/NumberKeypad.js.map +1 -0
  150. package/lib-commonjs/components/SudokuCell.js +245 -0
  151. package/lib-commonjs/components/SudokuCell.js.map +1 -0
  152. package/lib-commonjs/components/SudokuControls.js +145 -0
  153. package/lib-commonjs/components/SudokuControls.js.map +1 -0
  154. package/lib-commonjs/components/SudokuGrid.js +182 -0
  155. package/lib-commonjs/components/SudokuGrid.js.map +1 -0
  156. package/lib-commonjs/components/SudokuGridEntry.js +378 -0
  157. package/lib-commonjs/components/SudokuGridEntry.js.map +1 -0
  158. package/lib-commonjs/components/ValidationDisplay.js +100 -0
  159. package/lib-commonjs/components/ValidationDisplay.js.map +1 -0
  160. package/lib-commonjs/contexts/DiagnosticLoggerContext.js +104 -0
  161. package/lib-commonjs/contexts/DiagnosticLoggerContext.js.map +1 -0
  162. package/lib-commonjs/contexts/index.js +32 -0
  163. package/lib-commonjs/contexts/index.js.map +1 -0
  164. package/lib-commonjs/hooks/useCombinationElimination.js +126 -0
  165. package/lib-commonjs/hooks/useCombinationElimination.js.map +1 -0
  166. package/lib-commonjs/hooks/useKeyboardShortcut.js +58 -0
  167. package/lib-commonjs/hooks/useKeyboardShortcut.js.map +1 -0
  168. package/lib-commonjs/hooks/useKillerCombinations.js +68 -0
  169. package/lib-commonjs/hooks/useKillerCombinations.js.map +1 -0
  170. package/lib-commonjs/hooks/useLongPress.js +72 -0
  171. package/lib-commonjs/hooks/useLongPress.js.map +1 -0
  172. package/lib-commonjs/hooks/usePuzzleSession.js +401 -0
  173. package/lib-commonjs/hooks/usePuzzleSession.js.map +1 -0
  174. package/lib-commonjs/hooks/useResponsiveLayout.js +142 -0
  175. package/lib-commonjs/hooks/useResponsiveLayout.js.map +1 -0
  176. package/lib-commonjs/index.js +73 -0
  177. package/lib-commonjs/index.js.map +1 -0
  178. package/lib-commonjs/test/fixtures/killerSudokuPuzzles.js +60 -0
  179. package/lib-commonjs/test/fixtures/killerSudokuPuzzles.js.map +1 -0
  180. package/lib-commonjs/test/unit/components/SudokuGridEntry.testHelpers.js +125 -0
  181. package/lib-commonjs/test/unit/components/SudokuGridEntry.testHelpers.js.map +1 -0
  182. package/lib-commonjs/types/index.js +26 -0
  183. package/lib-commonjs/types/index.js.map +1 -0
  184. package/lib-commonjs/utils/CageLookupManager.js +287 -0
  185. package/lib-commonjs/utils/CageLookupManager.js.map +1 -0
  186. package/lib-commonjs/utils/CagePatternManager.js +143 -0
  187. package/lib-commonjs/utils/CagePatternManager.js.map +1 -0
  188. package/lib-commonjs/utils/puzzleDefinitionHelper.js +103 -0
  189. package/lib-commonjs/utils/puzzleDefinitionHelper.js.map +1 -0
  190. package/package.json +77 -0
@@ -0,0 +1,139 @@
1
+ /*
2
+ * MIT License
3
+ *
4
+ * Copyright (c) 2023 Erik Fortune
5
+ *
6
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ * of this software and associated documentation files (the "Software"), to deal
8
+ * in the Software without restriction, including without limitation the rights
9
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ * copies of the Software, and to permit persons to whom the Software is
11
+ * furnished to do so, subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be included in all
14
+ * copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ * SOFTWARE.
23
+ */
24
+ import { useState, useEffect } from 'react';
25
+ /**
26
+ * Breakpoints for responsive design
27
+ */
28
+ const BREAKPOINTS = {
29
+ mobile: 768,
30
+ tablet: 1024,
31
+ smallHeight: 600
32
+ };
33
+ /**
34
+ * Detect if device supports touch
35
+ */
36
+ function detectTouchSupport() {
37
+ return ('ontouchstart' in window ||
38
+ navigator.maxTouchPoints > 0 ||
39
+ (navigator.msMaxTouchPoints ?? 0) > 0);
40
+ }
41
+ /**
42
+ * Determine device type based on screen size and touch capability
43
+ */
44
+ function determineDeviceType(width, height, isTouch) {
45
+ if (width <= BREAKPOINTS.mobile) {
46
+ return 'mobile';
47
+ }
48
+ if (width <= BREAKPOINTS.tablet && isTouch) {
49
+ return 'tablet';
50
+ }
51
+ return 'desktop';
52
+ }
53
+ /**
54
+ * Determine optimal keypad layout mode
55
+ */
56
+ function determineKeypadLayoutMode(deviceType, orientation, width, height, isTouch) {
57
+ // Desktop: show overlay on demand or hidden by default
58
+ if (deviceType === 'desktop') {
59
+ return isTouch ? 'overlay' : 'hidden';
60
+ }
61
+ // Mobile portrait: side-by-side if width allows
62
+ if (deviceType === 'mobile' && orientation === 'portrait') {
63
+ return width >= 360 ? 'side-by-side' : 'stacked';
64
+ }
65
+ // Mobile landscape: stacked beside grid if height allows
66
+ if (deviceType === 'mobile' && orientation === 'landscape') {
67
+ return height >= BREAKPOINTS.smallHeight ? 'stacked' : 'overlay';
68
+ }
69
+ // Tablet: side-by-side in portrait, stacked in landscape
70
+ if (deviceType === 'tablet') {
71
+ return orientation === 'portrait' ? 'side-by-side' : 'stacked';
72
+ }
73
+ /* c8 ignore next 2 - defense in depth: all device types are explicitly handled above */
74
+ return 'overlay';
75
+ }
76
+ /**
77
+ * Hook to detect responsive layout information for optimal keypad display
78
+ * @param forceLayoutMode - Optional layout mode override for testing
79
+ * @public
80
+ */
81
+ export function useResponsiveLayout(forceLayoutMode) {
82
+ const [layoutInfo, setLayoutInfo] = useState(() => {
83
+ const width = window.innerWidth;
84
+ const height = window.innerHeight;
85
+ const isTouchDevice = detectTouchSupport();
86
+ const orientation = width > height ? 'landscape' : 'portrait';
87
+ const deviceType = determineDeviceType(width, height, isTouchDevice);
88
+ const keypadLayoutMode = forceLayoutMode ?? determineKeypadLayoutMode(deviceType, orientation, width, height, isTouchDevice);
89
+ return {
90
+ deviceType,
91
+ orientation,
92
+ keypadLayoutMode,
93
+ screenWidth: width,
94
+ screenHeight: height,
95
+ isTouchDevice,
96
+ isSmallScreen: width <= BREAKPOINTS.mobile,
97
+ hasSpaceForDualKeypads: width >= 480 || (orientation === 'landscape' && height >= BREAKPOINTS.smallHeight)
98
+ };
99
+ });
100
+ useEffect(() => {
101
+ // Don't set up listeners if layout mode is forced
102
+ if (forceLayoutMode) {
103
+ return;
104
+ }
105
+ function updateLayoutInfo() {
106
+ const width = window.innerWidth;
107
+ const height = window.innerHeight;
108
+ const isTouchDevice = detectTouchSupport();
109
+ const orientation = width > height ? 'landscape' : 'portrait';
110
+ const deviceType = determineDeviceType(width, height, isTouchDevice);
111
+ const keypadLayoutMode = determineKeypadLayoutMode(deviceType, orientation, width, height, isTouchDevice);
112
+ setLayoutInfo({
113
+ deviceType,
114
+ orientation,
115
+ keypadLayoutMode,
116
+ screenWidth: width,
117
+ screenHeight: height,
118
+ isTouchDevice,
119
+ isSmallScreen: width <= BREAKPOINTS.mobile,
120
+ hasSpaceForDualKeypads: width >= 480 || (orientation === 'landscape' && height >= BREAKPOINTS.smallHeight)
121
+ });
122
+ }
123
+ // Update on resize
124
+ window.addEventListener('resize', updateLayoutInfo);
125
+ // Update on orientation change
126
+ window.addEventListener('orientationchange', () => {
127
+ // Small delay to ensure screen dimensions are updated
128
+ setTimeout(updateLayoutInfo, 100);
129
+ });
130
+ // Initial update
131
+ updateLayoutInfo();
132
+ return () => {
133
+ window.removeEventListener('resize', updateLayoutInfo);
134
+ window.removeEventListener('orientationchange', updateLayoutInfo);
135
+ };
136
+ }, [forceLayoutMode]);
137
+ return layoutInfo;
138
+ }
139
+ //# sourceMappingURL=useResponsiveLayout.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useResponsiveLayout.js","sourceRoot":"","sources":["../../src/hooks/useResponsiveLayout.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * MIT License\n *\n * Copyright (c) 2023 Erik Fortune\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\nimport { useState, useEffect } from 'react';\n\n/**\n * Device type detection result\n * @public\n */\nexport type DeviceType = 'mobile' | 'tablet' | 'desktop';\n\n/**\n * Screen orientation\n * @public\n */\nexport type ScreenOrientation = 'portrait' | 'landscape';\n\n/**\n * Layout mode for dual keypad\n * @public\n */\nexport type KeypadLayoutMode = 'side-by-side' | 'stacked' | 'overlay' | 'hidden';\n\n/**\n * Responsive layout information\n * @public\n */\nexport interface IResponsiveLayoutInfo {\n /** Device type based on screen size and touch capability */\n readonly deviceType: DeviceType;\n\n /** Current screen orientation */\n readonly orientation: ScreenOrientation;\n\n /** Recommended keypad layout mode */\n readonly keypadLayoutMode: KeypadLayoutMode;\n\n /** Screen width in pixels */\n readonly screenWidth: number;\n\n /** Screen height in pixels */\n readonly screenHeight: number;\n\n /** Whether touch is supported */\n readonly isTouchDevice: boolean;\n\n /** Whether the screen is considered small */\n readonly isSmallScreen: boolean;\n\n /** Whether the screen has enough space for dual keypads */\n readonly hasSpaceForDualKeypads: boolean;\n}\n\n/**\n * Breakpoints for responsive design\n */\nconst BREAKPOINTS = {\n mobile: 768,\n tablet: 1024,\n smallHeight: 600\n} as const;\n\n/**\n * Detect if device supports touch\n */\nfunction detectTouchSupport(): boolean {\n return (\n 'ontouchstart' in window ||\n navigator.maxTouchPoints > 0 ||\n ((navigator as unknown as { msMaxTouchPoints?: number }).msMaxTouchPoints ?? 0) > 0\n );\n}\n\n/**\n * Determine device type based on screen size and touch capability\n */\nfunction determineDeviceType(width: number, height: number, isTouch: boolean): DeviceType {\n if (width <= BREAKPOINTS.mobile) {\n return 'mobile';\n }\n\n if (width <= BREAKPOINTS.tablet && isTouch) {\n return 'tablet';\n }\n\n return 'desktop';\n}\n\n/**\n * Determine optimal keypad layout mode\n */\nfunction determineKeypadLayoutMode(\n deviceType: DeviceType,\n orientation: ScreenOrientation,\n width: number,\n height: number,\n isTouch: boolean\n): KeypadLayoutMode {\n // Desktop: show overlay on demand or hidden by default\n if (deviceType === 'desktop') {\n return isTouch ? 'overlay' : 'hidden';\n }\n\n // Mobile portrait: side-by-side if width allows\n if (deviceType === 'mobile' && orientation === 'portrait') {\n return width >= 360 ? 'side-by-side' : 'stacked';\n }\n\n // Mobile landscape: stacked beside grid if height allows\n if (deviceType === 'mobile' && orientation === 'landscape') {\n return height >= BREAKPOINTS.smallHeight ? 'stacked' : 'overlay';\n }\n\n // Tablet: side-by-side in portrait, stacked in landscape\n if (deviceType === 'tablet') {\n return orientation === 'portrait' ? 'side-by-side' : 'stacked';\n }\n /* c8 ignore next 2 - defense in depth: all device types are explicitly handled above */\n return 'overlay';\n}\n\n/**\n * Hook to detect responsive layout information for optimal keypad display\n * @param forceLayoutMode - Optional layout mode override for testing\n * @public\n */\nexport function useResponsiveLayout(forceLayoutMode?: KeypadLayoutMode): IResponsiveLayoutInfo {\n const [layoutInfo, setLayoutInfo] = useState<IResponsiveLayoutInfo>(() => {\n const width = window.innerWidth;\n const height = window.innerHeight;\n const isTouchDevice = detectTouchSupport();\n const orientation: ScreenOrientation = width > height ? 'landscape' : 'portrait';\n const deviceType = determineDeviceType(width, height, isTouchDevice);\n const keypadLayoutMode =\n forceLayoutMode ?? determineKeypadLayoutMode(deviceType, orientation, width, height, isTouchDevice);\n\n return {\n deviceType,\n orientation,\n keypadLayoutMode,\n screenWidth: width,\n screenHeight: height,\n isTouchDevice,\n isSmallScreen: width <= BREAKPOINTS.mobile,\n hasSpaceForDualKeypads:\n width >= 480 || (orientation === 'landscape' && height >= BREAKPOINTS.smallHeight)\n };\n });\n\n useEffect(() => {\n // Don't set up listeners if layout mode is forced\n if (forceLayoutMode) {\n return;\n }\n\n function updateLayoutInfo(): void {\n const width = window.innerWidth;\n const height = window.innerHeight;\n const isTouchDevice = detectTouchSupport();\n const orientation: ScreenOrientation = width > height ? 'landscape' : 'portrait';\n const deviceType = determineDeviceType(width, height, isTouchDevice);\n const keypadLayoutMode = determineKeypadLayoutMode(\n deviceType,\n orientation,\n width,\n height,\n isTouchDevice\n );\n\n setLayoutInfo({\n deviceType,\n orientation,\n keypadLayoutMode,\n screenWidth: width,\n screenHeight: height,\n isTouchDevice,\n isSmallScreen: width <= BREAKPOINTS.mobile,\n hasSpaceForDualKeypads:\n width >= 480 || (orientation === 'landscape' && height >= BREAKPOINTS.smallHeight)\n });\n }\n\n // Update on resize\n window.addEventListener('resize', updateLayoutInfo);\n\n // Update on orientation change\n window.addEventListener('orientationchange', () => {\n // Small delay to ensure screen dimensions are updated\n setTimeout(updateLayoutInfo, 100);\n });\n\n // Initial update\n updateLayoutInfo();\n\n return () => {\n window.removeEventListener('resize', updateLayoutInfo);\n window.removeEventListener('orientationchange', updateLayoutInfo);\n };\n }, [forceLayoutMode]);\n\n return layoutInfo;\n}\n"]}
package/lib/index.d.ts ADDED
@@ -0,0 +1,28 @@
1
+ export { SudokuGridEntry } from './components/SudokuGridEntry';
2
+ export { SudokuGrid } from './components/SudokuGrid';
3
+ export { SudokuCell } from './components/SudokuCell';
4
+ export { SudokuControls } from './components/SudokuControls';
5
+ export { ValidationDisplay } from './components/ValidationDisplay';
6
+ export { CompactControlRibbon } from './components/CompactControlRibbon';
7
+ export { DualKeypad } from './components/DualKeypad';
8
+ export { NumberKeypad } from './components/NumberKeypad';
9
+ export { CageOverlay } from './components/CageOverlay';
10
+ export { CageSumIndicator } from './components/CageSumIndicator';
11
+ export * from './contexts/DiagnosticLoggerContext';
12
+ export { usePuzzleSession } from './hooks/usePuzzleSession';
13
+ export { useResponsiveLayout } from './hooks/useResponsiveLayout';
14
+ export { useKillerCombinations } from './hooks/useKillerCombinations';
15
+ export { useCombinationElimination } from './hooks/useCombinationElimination';
16
+ export { useKeyboardShortcut } from './hooks/useKeyboardShortcut';
17
+ export { KillerCombinationsExplorer } from './components/KillerCombinationsExplorer';
18
+ export { KillerCombinationsPanel } from './components/KillerCombinationsPanel';
19
+ export { KillerCombinationsModal } from './components/KillerCombinationsModal';
20
+ export { CombinationGrid } from './components/CombinationGrid';
21
+ export { CombinationCard } from './components/CombinationCard';
22
+ export type { IValidationError, ISudokuGridEntryProps, ISudokuGridProps, ICellDisplayInfo, ISudokuCellProps, ISudokuControlsProps, IValidationDisplayProps, ICageDisplayInfo, ICageOverlayProps, ICageSumIndicatorProps, IKillerCombinationsMode, ICombinationDisplayInfo, IEliminationState, IKillerCombinationsExplorerProps, ICombinationCardProps, ICombinationGridProps } from './types';
23
+ export type { ICompactControlRibbonProps } from './components/CompactControlRibbon';
24
+ export type { INumberKeypadProps } from './components/NumberKeypad';
25
+ export type { IDualKeypadProps } from './components/DualKeypad';
26
+ export type { DeviceType, ScreenOrientation, KeypadLayoutMode, IResponsiveLayoutInfo } from './hooks/useResponsiveLayout';
27
+ export type { IKeyboardShortcutOptions } from './hooks/useKeyboardShortcut';
28
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAyBA,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC/D,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AACzE,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAGjE,cAAc,oCAAoC,CAAC;AAGnD,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AACtE,OAAO,EAAE,yBAAyB,EAAE,MAAM,mCAAmC,CAAC;AAC9E,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAGlE,OAAO,EAAE,0BAA0B,EAAE,MAAM,yCAAyC,CAAC;AACrF,OAAO,EAAE,uBAAuB,EAAE,MAAM,sCAAsC,CAAC;AAC/E,OAAO,EAAE,uBAAuB,EAAE,MAAM,sCAAsC,CAAC;AAC/E,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC/D,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAG/D,YAAY,EACV,gBAAgB,EAChB,qBAAqB,EACrB,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,oBAAoB,EACpB,uBAAuB,EACvB,gBAAgB,EAChB,iBAAiB,EACjB,sBAAsB,EACtB,uBAAuB,EACvB,uBAAuB,EACvB,iBAAiB,EACjB,gCAAgC,EAChC,qBAAqB,EACrB,qBAAqB,EACtB,MAAM,SAAS,CAAC;AAGjB,YAAY,EAAE,0BAA0B,EAAE,MAAM,mCAAmC,CAAC;AAGpF,YAAY,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AACpE,YAAY,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAGhE,YAAY,EACV,UAAU,EACV,iBAAiB,EACjB,gBAAgB,EAChB,qBAAqB,EACtB,MAAM,6BAA6B,CAAC;AAGrC,YAAY,EAAE,wBAAwB,EAAE,MAAM,6BAA6B,CAAC"}
package/lib/index.js ADDED
@@ -0,0 +1,49 @@
1
+ /*
2
+ * MIT License
3
+ *
4
+ * Copyright (c) 2023 Erik Fortune
5
+ *
6
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ * of this software and associated documentation files (the "Software"), to deal
8
+ * in the Software without restriction, including without limitation the rights
9
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ * copies of the Software, and to permit persons to whom the Software is
11
+ * furnished to do so, subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be included in all
14
+ * copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ * SOFTWARE.
23
+ */
24
+ // Main components
25
+ export { SudokuGridEntry } from './components/SudokuGridEntry';
26
+ export { SudokuGrid } from './components/SudokuGrid';
27
+ export { SudokuCell } from './components/SudokuCell';
28
+ export { SudokuControls } from './components/SudokuControls';
29
+ export { ValidationDisplay } from './components/ValidationDisplay';
30
+ export { CompactControlRibbon } from './components/CompactControlRibbon';
31
+ export { DualKeypad } from './components/DualKeypad';
32
+ export { NumberKeypad } from './components/NumberKeypad';
33
+ export { CageOverlay } from './components/CageOverlay';
34
+ export { CageSumIndicator } from './components/CageSumIndicator';
35
+ // Contexts
36
+ export * from './contexts/DiagnosticLoggerContext';
37
+ // Hooks
38
+ export { usePuzzleSession } from './hooks/usePuzzleSession';
39
+ export { useResponsiveLayout } from './hooks/useResponsiveLayout';
40
+ export { useKillerCombinations } from './hooks/useKillerCombinations';
41
+ export { useCombinationElimination } from './hooks/useCombinationElimination';
42
+ export { useKeyboardShortcut } from './hooks/useKeyboardShortcut';
43
+ // Killer Combinations Components
44
+ export { KillerCombinationsExplorer } from './components/KillerCombinationsExplorer';
45
+ export { KillerCombinationsPanel } from './components/KillerCombinationsPanel';
46
+ export { KillerCombinationsModal } from './components/KillerCombinationsModal';
47
+ export { CombinationGrid } from './components/CombinationGrid';
48
+ export { CombinationCard } from './components/CombinationCard';
49
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,kBAAkB;AAClB,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC/D,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AACzE,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAEjE,WAAW;AACX,cAAc,oCAAoC,CAAC;AAEnD,QAAQ;AACR,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AACtE,OAAO,EAAE,yBAAyB,EAAE,MAAM,mCAAmC,CAAC;AAC9E,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAElE,iCAAiC;AACjC,OAAO,EAAE,0BAA0B,EAAE,MAAM,yCAAyC,CAAC;AACrF,OAAO,EAAE,uBAAuB,EAAE,MAAM,sCAAsC,CAAC;AAC/E,OAAO,EAAE,uBAAuB,EAAE,MAAM,sCAAsC,CAAC;AAC/E,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC/D,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC","sourcesContent":["/*\n * MIT License\n *\n * Copyright (c) 2023 Erik Fortune\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\n// Main components\nexport { SudokuGridEntry } from './components/SudokuGridEntry';\nexport { SudokuGrid } from './components/SudokuGrid';\nexport { SudokuCell } from './components/SudokuCell';\nexport { SudokuControls } from './components/SudokuControls';\nexport { ValidationDisplay } from './components/ValidationDisplay';\nexport { CompactControlRibbon } from './components/CompactControlRibbon';\nexport { DualKeypad } from './components/DualKeypad';\nexport { NumberKeypad } from './components/NumberKeypad';\nexport { CageOverlay } from './components/CageOverlay';\nexport { CageSumIndicator } from './components/CageSumIndicator';\n\n// Contexts\nexport * from './contexts/DiagnosticLoggerContext';\n\n// Hooks\nexport { usePuzzleSession } from './hooks/usePuzzleSession';\nexport { useResponsiveLayout } from './hooks/useResponsiveLayout';\nexport { useKillerCombinations } from './hooks/useKillerCombinations';\nexport { useCombinationElimination } from './hooks/useCombinationElimination';\nexport { useKeyboardShortcut } from './hooks/useKeyboardShortcut';\n\n// Killer Combinations Components\nexport { KillerCombinationsExplorer } from './components/KillerCombinationsExplorer';\nexport { KillerCombinationsPanel } from './components/KillerCombinationsPanel';\nexport { KillerCombinationsModal } from './components/KillerCombinationsModal';\nexport { CombinationGrid } from './components/CombinationGrid';\nexport { CombinationCard } from './components/CombinationCard';\n\n// Types\nexport type {\n IValidationError,\n ISudokuGridEntryProps,\n ISudokuGridProps,\n ICellDisplayInfo,\n ISudokuCellProps,\n ISudokuControlsProps,\n IValidationDisplayProps,\n ICageDisplayInfo,\n ICageOverlayProps,\n ICageSumIndicatorProps,\n IKillerCombinationsMode,\n ICombinationDisplayInfo,\n IEliminationState,\n IKillerCombinationsExplorerProps,\n ICombinationCardProps,\n ICombinationGridProps\n} from './types';\n\n// Compact control ribbon types\nexport type { ICompactControlRibbonProps } from './components/CompactControlRibbon';\n\n// Dual keypad types\nexport type { INumberKeypadProps } from './components/NumberKeypad';\nexport type { IDualKeypadProps } from './components/DualKeypad';\n\n// Responsive layout types\nexport type {\n DeviceType,\n ScreenOrientation,\n KeypadLayoutMode,\n IResponsiveLayoutInfo\n} from './hooks/useResponsiveLayout';\n\n// Keyboard shortcut types\nexport type { IKeyboardShortcutOptions } from './hooks/useKeyboardShortcut';\n"]}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Stable killer sudoku puzzle definitions for testing.
3
+ * These puzzles have been validated and are used across multiple test files
4
+ * to ensure consistency in testing killer sudoku functionality.
5
+ *
6
+ * @remarks
7
+ * Killer sudoku cells format: "GRID_MAPPING|CAGE_DEFINITIONS"
8
+ * - GRID_MAPPING: 81 characters mapping each cell to a cage ID (A-Z, a-z)
9
+ * - CAGE_DEFINITIONS: Comma-separated list of cageId+total (e.g., "A11,B09,C20")
10
+ *
11
+ * Example: "AABBCC....|A11,B09,C20,..."
12
+ * - Cells 0-1 are in cage A with total 11
13
+ * - Cells 2-3 are in cage B with total 09
14
+ * - Cells 4-5 are in cage C with total 20
15
+ *
16
+ * @public
17
+ */
18
+ /**
19
+ * Valid killer sudoku puzzle with complete cage definitions.
20
+ * This is a standard 9x9 killer sudoku with 27 cages.
21
+ */
22
+ export declare const VALID_KILLER_SUDOKU: string;
23
+ /**
24
+ * Killer sudoku puzzle metadata.
25
+ */
26
+ export interface IKillerSudokuFixture {
27
+ id: string;
28
+ description: string;
29
+ level: number;
30
+ cells: string;
31
+ }
32
+ /**
33
+ * Collection of validated killer sudoku puzzles for testing.
34
+ */
35
+ export declare const KILLER_SUDOKU_FIXTURES: IKillerSudokuFixture[];
36
+ //# sourceMappingURL=killerSudokuPuzzles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"killerSudokuPuzzles.d.ts","sourceRoot":"","sources":["../../../src/test/fixtures/killerSudokuPuzzles.ts"],"names":[],"mappings":"AAwBA;;;;;;;;;;;;;;;;GAgBG;AAEH;;;GAGG;AACH,eAAO,MAAM,mBAAmB,EAAE,MACuK,CAAC;AAE1M;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,eAAO,MAAM,sBAAsB,EAAE,oBAAoB,EAOxD,CAAC"}
@@ -0,0 +1,57 @@
1
+ /*
2
+ * MIT License
3
+ *
4
+ * Copyright (c) 2023 Erik Fortune
5
+ *
6
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ * of this software and associated documentation files (the "Software"), to deal
8
+ * in the Software without restriction, including without limitation the rights
9
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ * copies of the Software, and to permit persons to whom the Software is
11
+ * furnished to do so, subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be included in all
14
+ * copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ * SOFTWARE.
23
+ */
24
+ /**
25
+ * Stable killer sudoku puzzle definitions for testing.
26
+ * These puzzles have been validated and are used across multiple test files
27
+ * to ensure consistency in testing killer sudoku functionality.
28
+ *
29
+ * @remarks
30
+ * Killer sudoku cells format: "GRID_MAPPING|CAGE_DEFINITIONS"
31
+ * - GRID_MAPPING: 81 characters mapping each cell to a cage ID (A-Z, a-z)
32
+ * - CAGE_DEFINITIONS: Comma-separated list of cageId+total (e.g., "A11,B09,C20")
33
+ *
34
+ * Example: "AABBCC....|A11,B09,C20,..."
35
+ * - Cells 0-1 are in cage A with total 11
36
+ * - Cells 2-3 are in cage B with total 09
37
+ * - Cells 4-5 are in cage C with total 20
38
+ *
39
+ * @public
40
+ */
41
+ /**
42
+ * Valid killer sudoku puzzle with complete cage definitions.
43
+ * This is a standard 9x9 killer sudoku with 27 cages.
44
+ */
45
+ export const VALID_KILLER_SUDOKU = 'ABCCCDDDEABFFGGGDEHIJKGGLLLHIJKMGLNNHOPPMQQNROOSTMUVWRSSSTTUVWRXYTTTZZabXYYYcccab|A11,B09,C09,D20,E16,F17,G30,H17,I13,J09,K11,L16,M16,N11,O16,P07,Q11,R10,S14,T39,U08,V17,W16,X06,Y26,Z06,a09,b09,c11';
46
+ /**
47
+ * Collection of validated killer sudoku puzzles for testing.
48
+ */
49
+ export const KILLER_SUDOKU_FIXTURES = [
50
+ {
51
+ id: 'valid-killer-1',
52
+ description: 'Valid Killer Sudoku Puzzle',
53
+ level: 1,
54
+ cells: VALID_KILLER_SUDOKU
55
+ }
56
+ ];
57
+ //# sourceMappingURL=killerSudokuPuzzles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"killerSudokuPuzzles.js","sourceRoot":"","sources":["../../../src/test/fixtures/killerSudokuPuzzles.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH;;;;;;;;;;;;;;;;GAgBG;AAEH;;;GAGG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAC9B,uMAAuM,CAAC;AAY1M;;GAEG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAA2B;IAC5D;QACE,EAAE,EAAE,gBAAgB;QACpB,WAAW,EAAE,4BAA4B;QACzC,KAAK,EAAE,CAAC;QACR,KAAK,EAAE,mBAAmB;KAC3B;CACF,CAAC","sourcesContent":["/*\n * MIT License\n *\n * Copyright (c) 2023 Erik Fortune\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\n/**\n * Stable killer sudoku puzzle definitions for testing.\n * These puzzles have been validated and are used across multiple test files\n * to ensure consistency in testing killer sudoku functionality.\n *\n * @remarks\n * Killer sudoku cells format: \"GRID_MAPPING|CAGE_DEFINITIONS\"\n * - GRID_MAPPING: 81 characters mapping each cell to a cage ID (A-Z, a-z)\n * - CAGE_DEFINITIONS: Comma-separated list of cageId+total (e.g., \"A11,B09,C20\")\n *\n * Example: \"AABBCC....|A11,B09,C20,...\"\n * - Cells 0-1 are in cage A with total 11\n * - Cells 2-3 are in cage B with total 09\n * - Cells 4-5 are in cage C with total 20\n *\n * @public\n */\n\n/**\n * Valid killer sudoku puzzle with complete cage definitions.\n * This is a standard 9x9 killer sudoku with 27 cages.\n */\nexport const VALID_KILLER_SUDOKU: string =\n 'ABCCCDDDEABFFGGGDEHIJKGGLLLHIJKMGLNNHOPPMQQNROOSTMUVWRSSSTTUVWRXYTTTZZabXYYYcccab|A11,B09,C09,D20,E16,F17,G30,H17,I13,J09,K11,L16,M16,N11,O16,P07,Q11,R10,S14,T39,U08,V17,W16,X06,Y26,Z06,a09,b09,c11';\n\n/**\n * Killer sudoku puzzle metadata.\n */\nexport interface IKillerSudokuFixture {\n id: string;\n description: string;\n level: number;\n cells: string;\n}\n\n/**\n * Collection of validated killer sudoku puzzles for testing.\n */\nexport const KILLER_SUDOKU_FIXTURES: IKillerSudokuFixture[] = [\n {\n id: 'valid-killer-1',\n description: 'Valid Killer Sudoku Puzzle',\n level: 1,\n cells: VALID_KILLER_SUDOKU\n }\n];\n"]}
@@ -0,0 +1,22 @@
1
+ import { RenderResult, RenderOptions } from '@testing-library/react';
2
+ import { IPuzzleDefinition } from '@fgv/ts-sudoku-lib';
3
+ import { createPuzzleDefinition } from '../../../utils/puzzleDefinitionHelper';
4
+ export type { IPuzzleDefinition };
5
+ export { createPuzzleDefinition };
6
+ export declare function customRender(ui: React.ReactElement, options?: RenderOptions): RenderResult;
7
+ export declare function advanceByLongPress(ms?: number): void;
8
+ export declare function setWindowDimensions(width: number, height: number): void;
9
+ export declare function renderWithMobilePortrait(component: React.ReactElement, options?: RenderOptions): RenderResult;
10
+ export declare function renderWithDesktop(component: React.ReactElement, options?: RenderOptions): RenderResult;
11
+ export declare const mockMatchMedia: (query: string) => MediaQueryList;
12
+ export declare const validPuzzleDescription: IPuzzleDefinition;
13
+ export declare const simplePuzzleDescription: IPuzzleDefinition;
14
+ export declare const sudokuXPuzzleDescription: IPuzzleDefinition;
15
+ export declare const invalidPuzzleDescription: IPuzzleDefinition;
16
+ export declare const defaultProps: {
17
+ initialPuzzleDescription: undefined;
18
+ onStateChange: undefined;
19
+ onValidationErrors: undefined;
20
+ className: undefined;
21
+ };
22
+ //# sourceMappingURL=SudokuGridEntry.testHelpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SudokuGridEntry.testHelpers.d.ts","sourceRoot":"","sources":["../../../../src/test/unit/components/SudokuGridEntry.testHelpers.ts"],"names":[],"mappings":"AAwBA,OAAO,EAAU,YAAY,EAAE,aAAa,EAAO,MAAM,wBAAwB,CAAC;AAClF,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAE,sBAAsB,EAAE,MAAM,uCAAuC,CAAC;AAG/E,YAAY,EAAE,iBAAiB,EAAE,CAAC;AAClC,OAAO,EAAE,sBAAsB,EAAE,CAAC;AAGlC,wBAAgB,YAAY,CAAC,EAAE,EAAE,KAAK,CAAC,YAAY,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,YAAY,CAE1F;AAGD,wBAAgB,kBAAkB,CAAC,EAAE,GAAE,MAAY,GAAG,IAAI,CAIzD;AAGD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAWvE;AAGD,wBAAgB,wBAAwB,CACtC,SAAS,EAAE,KAAK,CAAC,YAAY,EAC7B,OAAO,CAAC,EAAE,aAAa,GACtB,YAAY,CAGd;AAGD,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,KAAK,CAAC,YAAY,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,YAAY,CAGtG;AAGD,eAAO,MAAM,cAAc,GAAI,OAAO,MAAM,KAAG,cAW9C,CAAC;AAGF,eAAO,MAAM,sBAAsB,EAAE,iBAQnC,CAAC;AAEH,eAAO,MAAM,uBAAuB,EAAE,iBAQpC,CAAC;AAEH,eAAO,MAAM,wBAAwB,EAAE,iBAQrC,CAAC;AAEH,eAAO,MAAM,wBAAwB,EAAE,iBAQrC,CAAC;AAEH,eAAO,MAAM,YAAY,EAAE;IACzB,wBAAwB,EAAE,SAAS,CAAC;IACpC,aAAa,EAAE,SAAS,CAAC;IACzB,kBAAkB,EAAE,SAAS,CAAC;IAC9B,SAAS,EAAE,SAAS,CAAC;CAMtB,CAAC"}
@@ -0,0 +1,116 @@
1
+ /*
2
+ * MIT License
3
+ *
4
+ * Copyright (c) 2023 Erik Fortune
5
+ *
6
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ * of this software and associated documentation files (the "Software"), to deal
8
+ * in the Software without restriction, including without limitation the rights
9
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ * copies of the Software, and to permit persons to whom the Software is
11
+ * furnished to do so, subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be included in all
14
+ * copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ * SOFTWARE.
23
+ */
24
+ import { render, act } from '@testing-library/react';
25
+ import { createPuzzleDefinition } from '../../../utils/puzzleDefinitionHelper';
26
+ export { createPuzzleDefinition };
27
+ // Custom render function - just use standard render
28
+ export function customRender(ui, options) {
29
+ return render(ui, options);
30
+ }
31
+ // Helper function to simulate long press delay using fake timers
32
+ export function advanceByLongPress(ms = 600) {
33
+ act(() => {
34
+ jest.advanceTimersByTime(ms);
35
+ });
36
+ }
37
+ // Helper to set window dimensions for different device types
38
+ export function setWindowDimensions(width, height) {
39
+ Object.defineProperty(window, 'innerWidth', {
40
+ writable: true,
41
+ configurable: true,
42
+ value: width
43
+ });
44
+ Object.defineProperty(window, 'innerHeight', {
45
+ writable: true,
46
+ configurable: true,
47
+ value: height
48
+ });
49
+ }
50
+ // Helper to render with mobile portrait layout (side-by-side keypads)
51
+ export function renderWithMobilePortrait(component, options) {
52
+ setWindowDimensions(375, 667);
53
+ return customRender(component, options);
54
+ }
55
+ // Helper to render with desktop layout (hidden/overlay keypads)
56
+ export function renderWithDesktop(component, options) {
57
+ setWindowDimensions(1024, 768);
58
+ return customRender(component, options);
59
+ }
60
+ // Mock window.matchMedia for responsive layout tests
61
+ export const mockMatchMedia = (query) => {
62
+ return {
63
+ matches: false,
64
+ media: query,
65
+ onchange: null,
66
+ addListener: jest.fn(),
67
+ removeListener: jest.fn(),
68
+ addEventListener: jest.fn(),
69
+ removeEventListener: jest.fn(),
70
+ dispatchEvent: jest.fn()
71
+ };
72
+ };
73
+ // Test puzzle definitions
74
+ export const validPuzzleDescription = createPuzzleDefinition({
75
+ id: 'test-puzzle',
76
+ description: 'Test Puzzle',
77
+ type: 'sudoku',
78
+ level: 1,
79
+ totalRows: 9,
80
+ totalColumns: 9,
81
+ cells: '123456789456789123789123456234567891567891234891234567345678912678912345912345678'
82
+ });
83
+ export const simplePuzzleDescription = createPuzzleDefinition({
84
+ id: 'simple-puzzle',
85
+ description: 'Simple Test Puzzle',
86
+ type: 'sudoku',
87
+ level: 1,
88
+ totalRows: 9,
89
+ totalColumns: 9,
90
+ cells: '1' + '.'.repeat(80) // 81 total characters
91
+ });
92
+ export const sudokuXPuzzleDescription = createPuzzleDefinition({
93
+ id: 'sudoku-x-puzzle',
94
+ description: 'Sudoku-X Test Puzzle',
95
+ type: 'sudoku-x',
96
+ level: 1,
97
+ totalRows: 9,
98
+ totalColumns: 9,
99
+ cells: '.'.repeat(81)
100
+ });
101
+ export const invalidPuzzleDescription = createPuzzleDefinition({
102
+ id: 'invalid-puzzle',
103
+ description: 'Invalid Puzzle',
104
+ type: 'sudoku',
105
+ level: 1,
106
+ totalRows: 9,
107
+ totalColumns: 9,
108
+ cells: 'invalid' // Too short
109
+ });
110
+ export const defaultProps = {
111
+ initialPuzzleDescription: undefined,
112
+ onStateChange: undefined,
113
+ onValidationErrors: undefined,
114
+ className: undefined
115
+ };
116
+ //# sourceMappingURL=SudokuGridEntry.testHelpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SudokuGridEntry.testHelpers.js","sourceRoot":"","sources":["../../../../src/test/unit/components/SudokuGridEntry.testHelpers.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * MIT License\n *\n * Copyright (c) 2023 Erik Fortune\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\nimport { render, RenderResult, RenderOptions, act } from '@testing-library/react';\nimport { IPuzzleDefinition } from '@fgv/ts-sudoku-lib';\nimport { createPuzzleDefinition } from '../../../utils/puzzleDefinitionHelper';\n\n// Re-export for use in test files\nexport type { IPuzzleDefinition };\nexport { createPuzzleDefinition };\n\n// Custom render function - just use standard render\nexport function customRender(ui: React.ReactElement, options?: RenderOptions): RenderResult {\n return render(ui, options);\n}\n\n// Helper function to simulate long press delay using fake timers\nexport function advanceByLongPress(ms: number = 600): void {\n act(() => {\n jest.advanceTimersByTime(ms);\n });\n}\n\n// Helper to set window dimensions for different device types\nexport function setWindowDimensions(width: number, height: number): void {\n Object.defineProperty(window, 'innerWidth', {\n writable: true,\n configurable: true,\n value: width\n });\n Object.defineProperty(window, 'innerHeight', {\n writable: true,\n configurable: true,\n value: height\n });\n}\n\n// Helper to render with mobile portrait layout (side-by-side keypads)\nexport function renderWithMobilePortrait(\n component: React.ReactElement,\n options?: RenderOptions\n): RenderResult {\n setWindowDimensions(375, 667);\n return customRender(component, options);\n}\n\n// Helper to render with desktop layout (hidden/overlay keypads)\nexport function renderWithDesktop(component: React.ReactElement, options?: RenderOptions): RenderResult {\n setWindowDimensions(1024, 768);\n return customRender(component, options);\n}\n\n// Mock window.matchMedia for responsive layout tests\nexport const mockMatchMedia = (query: string): MediaQueryList => {\n return {\n matches: false,\n media: query,\n onchange: null,\n addListener: jest.fn(),\n removeListener: jest.fn(),\n addEventListener: jest.fn(),\n removeEventListener: jest.fn(),\n dispatchEvent: jest.fn()\n } as unknown as MediaQueryList;\n};\n\n// Test puzzle definitions\nexport const validPuzzleDescription: IPuzzleDefinition = createPuzzleDefinition({\n id: 'test-puzzle',\n description: 'Test Puzzle',\n type: 'sudoku',\n level: 1,\n totalRows: 9,\n totalColumns: 9,\n cells: '123456789456789123789123456234567891567891234891234567345678912678912345912345678'\n});\n\nexport const simplePuzzleDescription: IPuzzleDefinition = createPuzzleDefinition({\n id: 'simple-puzzle',\n description: 'Simple Test Puzzle',\n type: 'sudoku',\n level: 1,\n totalRows: 9,\n totalColumns: 9,\n cells: '1' + '.'.repeat(80) // 81 total characters\n});\n\nexport const sudokuXPuzzleDescription: IPuzzleDefinition = createPuzzleDefinition({\n id: 'sudoku-x-puzzle',\n description: 'Sudoku-X Test Puzzle',\n type: 'sudoku-x',\n level: 1,\n totalRows: 9,\n totalColumns: 9,\n cells: '.'.repeat(81)\n});\n\nexport const invalidPuzzleDescription: IPuzzleDefinition = createPuzzleDefinition({\n id: 'invalid-puzzle',\n description: 'Invalid Puzzle',\n type: 'sudoku',\n level: 1,\n totalRows: 9,\n totalColumns: 9,\n cells: 'invalid' // Too short\n});\n\nexport const defaultProps: {\n initialPuzzleDescription: undefined;\n onStateChange: undefined;\n onValidationErrors: undefined;\n className: undefined;\n} = {\n initialPuzzleDescription: undefined,\n onStateChange: undefined,\n onValidationErrors: undefined,\n className: undefined\n};\n"]}
@@ -0,0 +1,11 @@
1
+ // This file is read by tools that parse documentation comments conforming to the TSDoc standard.
2
+ // It should be published with your NPM package. It should not be tracked by Git.
3
+ {
4
+ "tsdocVersion": "0.12",
5
+ "toolPackages": [
6
+ {
7
+ "packageName": "@microsoft/api-extractor",
8
+ "packageVersion": "7.52.12"
9
+ }
10
+ ]
11
+ }