@fgv/ts-sudoku-ui 5.1.0-2 → 5.1.0-21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/eslint.config.js +18 -0
- package/lib/tsdoc-metadata.json +1 -1
- package/package.json +22 -21
- package/.test-failures-catalog.md +0 -470
- package/CHANGELOG.md +0 -11
- package/CSS_MODULES_MIGRATION_SUMMARY.md +0 -189
- package/dist/ts-sudoku-ui.d.ts +0 -668
- package/etc/ts-sudoku-ui.api.md +0 -481
- package/lib/test/fixtures/killerSudokuPuzzles.d.ts +0 -36
- package/lib/test/fixtures/killerSudokuPuzzles.d.ts.map +0 -1
- package/lib/test/fixtures/killerSudokuPuzzles.js +0 -57
- package/lib/test/fixtures/killerSudokuPuzzles.js.map +0 -1
- package/lib/test/unit/components/SudokuGridEntry.testHelpers.d.ts +0 -22
- package/lib/test/unit/components/SudokuGridEntry.testHelpers.d.ts.map +0 -1
- package/lib/test/unit/components/SudokuGridEntry.testHelpers.js +0 -116
- package/lib/test/unit/components/SudokuGridEntry.testHelpers.js.map +0 -1
- package/lib-commonjs/components/CageOverlay.js +0 -138
- package/lib-commonjs/components/CageOverlay.js.map +0 -1
- package/lib-commonjs/components/CageSumIndicator.js +0 -71
- package/lib-commonjs/components/CageSumIndicator.js.map +0 -1
- package/lib-commonjs/components/CombinationCard.js +0 -68
- package/lib-commonjs/components/CombinationCard.js.map +0 -1
- package/lib-commonjs/components/CombinationGrid.js +0 -56
- package/lib-commonjs/components/CombinationGrid.js.map +0 -1
- package/lib-commonjs/components/CompactControlRibbon.js +0 -146
- package/lib-commonjs/components/CompactControlRibbon.js.map +0 -1
- package/lib-commonjs/components/DualKeypad.js +0 -142
- package/lib-commonjs/components/DualKeypad.js.map +0 -1
- package/lib-commonjs/components/KillerCombinationsExplorer.js +0 -74
- package/lib-commonjs/components/KillerCombinationsExplorer.js.map +0 -1
- package/lib-commonjs/components/KillerCombinationsModal.js +0 -140
- package/lib-commonjs/components/KillerCombinationsModal.js.map +0 -1
- package/lib-commonjs/components/KillerCombinationsPanel.js +0 -84
- package/lib-commonjs/components/KillerCombinationsPanel.js.map +0 -1
- package/lib-commonjs/components/NumberKeypad.js +0 -186
- package/lib-commonjs/components/NumberKeypad.js.map +0 -1
- package/lib-commonjs/components/SudokuCell.js +0 -245
- package/lib-commonjs/components/SudokuCell.js.map +0 -1
- package/lib-commonjs/components/SudokuControls.js +0 -145
- package/lib-commonjs/components/SudokuControls.js.map +0 -1
- package/lib-commonjs/components/SudokuGrid.js +0 -182
- package/lib-commonjs/components/SudokuGrid.js.map +0 -1
- package/lib-commonjs/components/SudokuGridEntry.js +0 -378
- package/lib-commonjs/components/SudokuGridEntry.js.map +0 -1
- package/lib-commonjs/components/ValidationDisplay.js +0 -100
- package/lib-commonjs/components/ValidationDisplay.js.map +0 -1
- package/lib-commonjs/contexts/DiagnosticLoggerContext.js +0 -104
- package/lib-commonjs/contexts/DiagnosticLoggerContext.js.map +0 -1
- package/lib-commonjs/contexts/index.js +0 -32
- package/lib-commonjs/contexts/index.js.map +0 -1
- package/lib-commonjs/hooks/useCombinationElimination.js +0 -126
- package/lib-commonjs/hooks/useCombinationElimination.js.map +0 -1
- package/lib-commonjs/hooks/useKeyboardShortcut.js +0 -58
- package/lib-commonjs/hooks/useKeyboardShortcut.js.map +0 -1
- package/lib-commonjs/hooks/useKillerCombinations.js +0 -68
- package/lib-commonjs/hooks/useKillerCombinations.js.map +0 -1
- package/lib-commonjs/hooks/useLongPress.js +0 -72
- package/lib-commonjs/hooks/useLongPress.js.map +0 -1
- package/lib-commonjs/hooks/usePuzzleSession.js +0 -401
- package/lib-commonjs/hooks/usePuzzleSession.js.map +0 -1
- package/lib-commonjs/hooks/useResponsiveLayout.js +0 -142
- package/lib-commonjs/hooks/useResponsiveLayout.js.map +0 -1
- package/lib-commonjs/index.browser.js +0 -29
- package/lib-commonjs/index.browser.js.map +0 -1
- package/lib-commonjs/index.js +0 -73
- package/lib-commonjs/index.js.map +0 -1
- package/lib-commonjs/test/fixtures/killerSudokuPuzzles.js +0 -60
- package/lib-commonjs/test/fixtures/killerSudokuPuzzles.js.map +0 -1
- package/lib-commonjs/test/unit/components/SudokuGridEntry.testHelpers.js +0 -125
- package/lib-commonjs/test/unit/components/SudokuGridEntry.testHelpers.js.map +0 -1
- package/lib-commonjs/types/index.js +0 -26
- package/lib-commonjs/types/index.js.map +0 -1
- package/lib-commonjs/utils/CageLookupManager.js +0 -287
- package/lib-commonjs/utils/CageLookupManager.js.map +0 -1
- package/lib-commonjs/utils/CagePatternManager.js +0 -143
- package/lib-commonjs/utils/CagePatternManager.js.map +0 -1
- package/lib-commonjs/utils/puzzleDefinitionHelper.js +0 -103
- package/lib-commonjs/utils/puzzleDefinitionHelper.js.map +0 -1
|
@@ -1,116 +0,0 @@
|
|
|
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
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"SudokuGridEntry.testHelpers.js","sourceRoot":"","sources":["../../../../src/test/unit/components/SudokuGridEntry.testHelpers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,MAAM,EAA+B,GAAG,EAAE,MAAM,wBAAwB,CAAC;AAElF,OAAO,EAAE,sBAAsB,EAAE,MAAM,uCAAuC,CAAC;AAI/E,OAAO,EAAE,sBAAsB,EAAE,CAAC;AAElC,oDAAoD;AACpD,MAAM,UAAU,YAAY,CAAC,EAAsB,EAAE,OAAuB;IAC1E,OAAO,MAAM,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;AAC7B,CAAC;AAED,iEAAiE;AACjE,MAAM,UAAU,kBAAkB,CAAC,KAAa,GAAG;IACjD,GAAG,CAAC,GAAG,EAAE;QACP,IAAI,CAAC,mBAAmB,CAAC,EAAE,CAAC,CAAC;IAC/B,CAAC,CAAC,CAAC;AACL,CAAC;AAED,6DAA6D;AAC7D,MAAM,UAAU,mBAAmB,CAAC,KAAa,EAAE,MAAc;IAC/D,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,YAAY,EAAE;QAC1C,QAAQ,EAAE,IAAI;QACd,YAAY,EAAE,IAAI;QAClB,KAAK,EAAE,KAAK;KACb,CAAC,CAAC;IACH,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,aAAa,EAAE;QAC3C,QAAQ,EAAE,IAAI;QACd,YAAY,EAAE,IAAI;QAClB,KAAK,EAAE,MAAM;KACd,CAAC,CAAC;AACL,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,wBAAwB,CACtC,SAA6B,EAC7B,OAAuB;IAEvB,mBAAmB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IAC9B,OAAO,YAAY,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;AAC1C,CAAC;AAED,gEAAgE;AAChE,MAAM,UAAU,iBAAiB,CAAC,SAA6B,EAAE,OAAuB;IACtF,mBAAmB,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IAC/B,OAAO,YAAY,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;AAC1C,CAAC;AAED,qDAAqD;AACrD,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,KAAa,EAAkB,EAAE;IAC9D,OAAO;QACL,OAAO,EAAE,KAAK;QACd,KAAK,EAAE,KAAK;QACZ,QAAQ,EAAE,IAAI;QACd,WAAW,EAAE,IAAI,CAAC,EAAE,EAAE;QACtB,cAAc,EAAE,IAAI,CAAC,EAAE,EAAE;QACzB,gBAAgB,EAAE,IAAI,CAAC,EAAE,EAAE;QAC3B,mBAAmB,EAAE,IAAI,CAAC,EAAE,EAAE;QAC9B,aAAa,EAAE,IAAI,CAAC,EAAE,EAAE;KACI,CAAC;AACjC,CAAC,CAAC;AAEF,0BAA0B;AAC1B,MAAM,CAAC,MAAM,sBAAsB,GAAsB,sBAAsB,CAAC;IAC9E,EAAE,EAAE,aAAa;IACjB,WAAW,EAAE,aAAa;IAC1B,IAAI,EAAE,QAAQ;IACd,KAAK,EAAE,CAAC;IACR,SAAS,EAAE,CAAC;IACZ,YAAY,EAAE,CAAC;IACf,KAAK,EAAE,mFAAmF;CAC3F,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,uBAAuB,GAAsB,sBAAsB,CAAC;IAC/E,EAAE,EAAE,eAAe;IACnB,WAAW,EAAE,oBAAoB;IACjC,IAAI,EAAE,QAAQ;IACd,KAAK,EAAE,CAAC;IACR,SAAS,EAAE,CAAC;IACZ,YAAY,EAAE,CAAC;IACf,KAAK,EAAE,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,sBAAsB;CACnD,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,wBAAwB,GAAsB,sBAAsB,CAAC;IAChF,EAAE,EAAE,iBAAiB;IACrB,WAAW,EAAE,sBAAsB;IACnC,IAAI,EAAE,UAAU;IAChB,KAAK,EAAE,CAAC;IACR,SAAS,EAAE,CAAC;IACZ,YAAY,EAAE,CAAC;IACf,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;CACtB,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,wBAAwB,GAAsB,sBAAsB,CAAC;IAChF,EAAE,EAAE,gBAAgB;IACpB,WAAW,EAAE,gBAAgB;IAC7B,IAAI,EAAE,QAAQ;IACd,KAAK,EAAE,CAAC;IACR,SAAS,EAAE,CAAC;IACZ,YAAY,EAAE,CAAC;IACf,KAAK,EAAE,SAAS,CAAC,YAAY;CAC9B,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,YAAY,GAKrB;IACF,wBAAwB,EAAE,SAAS;IACnC,aAAa,EAAE,SAAS;IACxB,kBAAkB,EAAE,SAAS;IAC7B,SAAS,EAAE,SAAS;CACrB,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\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"]}
|
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/*
|
|
3
|
-
* MIT License
|
|
4
|
-
*
|
|
5
|
-
* Copyright (c) 2023 Erik Fortune
|
|
6
|
-
*
|
|
7
|
-
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
8
|
-
* of this software and associated documentation files (the "Software"), to deal
|
|
9
|
-
* in the Software without restriction, including without limitation the rights
|
|
10
|
-
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
11
|
-
* copies of the Software, and to permit persons to whom the Software is
|
|
12
|
-
* furnished to do so, subject to the following conditions:
|
|
13
|
-
*
|
|
14
|
-
* The above copyright notice and this permission notice shall be included in all
|
|
15
|
-
* copies or substantial portions of the Software.
|
|
16
|
-
*
|
|
17
|
-
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
18
|
-
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
19
|
-
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
20
|
-
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
21
|
-
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
22
|
-
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
23
|
-
* SOFTWARE.
|
|
24
|
-
*/
|
|
25
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
-
exports.CageOverlay = void 0;
|
|
27
|
-
const tslib_1 = require("tslib");
|
|
28
|
-
const react_1 = tslib_1.__importStar(require("react"));
|
|
29
|
-
const ts_sudoku_lib_1 = require("@fgv/ts-sudoku-lib");
|
|
30
|
-
const CageSumIndicator_1 = require("./CageSumIndicator");
|
|
31
|
-
const CagePatternManager_1 = require("../utils/CagePatternManager");
|
|
32
|
-
const CageLookupManager_1 = require("../utils/CageLookupManager");
|
|
33
|
-
const DiagnosticLoggerContext_1 = require("../contexts/DiagnosticLoggerContext");
|
|
34
|
-
/**
|
|
35
|
-
* Helper to get cell position from CellId
|
|
36
|
-
*/
|
|
37
|
-
function getTopLeftPosition(cellIds, log, numRows, numColumns) {
|
|
38
|
-
if (cellIds.length === 0)
|
|
39
|
-
return { top: 0, left: 0 };
|
|
40
|
-
const positions = cellIds.map((cellId) => {
|
|
41
|
-
const parsed = (0, ts_sudoku_lib_1.parseCellId)(cellId);
|
|
42
|
-
if (parsed && parsed.row >= 0 && parsed.row < numRows && parsed.col >= 0 && parsed.col < numColumns) {
|
|
43
|
-
return parsed;
|
|
44
|
-
}
|
|
45
|
-
// Fallback - should not happen with valid CellIds
|
|
46
|
-
log.warn(`Invalid CellId format: ${cellId}`);
|
|
47
|
-
return { row: 0, col: 0 };
|
|
48
|
-
});
|
|
49
|
-
// Find the top-left most cell
|
|
50
|
-
let minRow = positions[0].row;
|
|
51
|
-
let minCol = positions[0].col;
|
|
52
|
-
positions.forEach(({ row, col }) => {
|
|
53
|
-
if (row < minRow || (row === minRow && col < minCol)) {
|
|
54
|
-
minRow = row;
|
|
55
|
-
minCol = col;
|
|
56
|
-
}
|
|
57
|
-
});
|
|
58
|
-
// Convert grid coordinates to percentage-based positioning
|
|
59
|
-
const cellWidthPercent = 100 / numColumns;
|
|
60
|
-
const cellHeightPercent = 100 / numRows;
|
|
61
|
-
return {
|
|
62
|
-
top: minRow * cellHeightPercent + 0.5, // Small offset from corner
|
|
63
|
-
left: minCol * cellWidthPercent + 0.5 // Small offset from corner
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
/**
|
|
67
|
-
* Component for rendering cage boundaries and sum indicators in Killer Sudoku
|
|
68
|
-
* @public
|
|
69
|
-
*/
|
|
70
|
-
const CageOverlay = ({ cages, gridSize, cellSize, numRows = 9, numColumns = 9, className }) => {
|
|
71
|
-
const log = (0, DiagnosticLoggerContext_1.useDiagnosticLogger)();
|
|
72
|
-
// Initialize managers once
|
|
73
|
-
const patternManager = (0, react_1.useMemo)(() => new CagePatternManager_1.CagePatternManager(), []);
|
|
74
|
-
const lookupManager = (0, react_1.useMemo)(() => CageLookupManager_1.CageLookupManager.getInstance(), []);
|
|
75
|
-
// Debug logging
|
|
76
|
-
(0, react_1.useEffect)(() => {
|
|
77
|
-
log.info('CageOverlay rendered', {
|
|
78
|
-
cageCount: cages.length,
|
|
79
|
-
gridSize,
|
|
80
|
-
cellSize,
|
|
81
|
-
cages: cages.map((c) => ({ id: c.cage.id, cellIds: c.cage.cellIds }))
|
|
82
|
-
});
|
|
83
|
-
}, [cages, gridSize, cellSize, log]);
|
|
84
|
-
// Calculate SVG viewBox and class names
|
|
85
|
-
const viewBox = `0 0 ${gridSize.width} ${gridSize.height}`;
|
|
86
|
-
const overlayClasses = (0, react_1.useMemo)(() => {
|
|
87
|
-
const classes = ['cage-overlay'];
|
|
88
|
-
if (className)
|
|
89
|
-
classes.push(className);
|
|
90
|
-
return classes.join(' ');
|
|
91
|
-
}, [className]);
|
|
92
|
-
return (react_1.default.createElement("div", { className: `
|
|
93
|
-
${overlayClasses}
|
|
94
|
-
absolute inset-0 w-full h-full pointer-events-none
|
|
95
|
-
`, style: { zIndex: 1000 }, "data-testid": "cage-overlay" },
|
|
96
|
-
react_1.default.createElement("svg", { width: "100%", height: "100%", viewBox: viewBox, className: "absolute inset-0", preserveAspectRatio: "none" }, cages.map((cageInfo, index) => {
|
|
97
|
-
const { cage, isHighlighted, isValid } = cageInfo;
|
|
98
|
-
// Build occupied cells set for this cage
|
|
99
|
-
const occupiedCells = patternManager.buildOccupiedCellsSet(cage.cellIds);
|
|
100
|
-
// Render each cell with connected borders
|
|
101
|
-
return cage.cellIds.map((cellId, cellIndex) => {
|
|
102
|
-
// Parse cell position using centralized function
|
|
103
|
-
const parsed = (0, ts_sudoku_lib_1.parseCellId)(cellId);
|
|
104
|
-
let row = 0, col = 0;
|
|
105
|
-
if (parsed &&
|
|
106
|
-
parsed.row >= 0 &&
|
|
107
|
-
parsed.row < numRows &&
|
|
108
|
-
parsed.col >= 0 &&
|
|
109
|
-
parsed.col < numColumns) {
|
|
110
|
-
row = parsed.row;
|
|
111
|
-
col = parsed.col;
|
|
112
|
-
}
|
|
113
|
-
else {
|
|
114
|
-
log.warn(`Invalid CellId format: ${cellId}`);
|
|
115
|
-
}
|
|
116
|
-
const x = col * cellSize;
|
|
117
|
-
const y = row * cellSize;
|
|
118
|
-
// Analyze neighbors and get pattern
|
|
119
|
-
const neighbors = patternManager.analyzeNeighbors(cellId, occupiedCells);
|
|
120
|
-
const neighborPattern = lookupManager.neighborsToPattern(neighbors);
|
|
121
|
-
const pathData = lookupManager.getPatternSVG(neighborPattern);
|
|
122
|
-
// Skip if no path data
|
|
123
|
-
if (!pathData) {
|
|
124
|
-
return null;
|
|
125
|
-
}
|
|
126
|
-
// Render connected border pattern
|
|
127
|
-
return (react_1.default.createElement("g", { key: /* c8 ignore next 1 - defense in depth */ `${cage.id ?? index}-${cellIndex}`, transform: `translate(${x}, ${y}) scale(${cellSize})` },
|
|
128
|
-
react_1.default.createElement("path", { d: pathData, stroke: isHighlighted ? '#1e40af' : isValid ? '#2563eb' : '#dc2626', strokeWidth: 0.8 / cellSize, strokeDasharray: "6 4", strokeLinecap: "round", strokeLinejoin: "round", fill: "none", opacity: "1.0", vectorEffect: "non-scaling-stroke" })));
|
|
129
|
-
});
|
|
130
|
-
})),
|
|
131
|
-
cages.map((cageInfo, index) => {
|
|
132
|
-
const { cage, currentSum, isComplete, isValid } = cageInfo;
|
|
133
|
-
const position = getTopLeftPosition(cage.cellIds, log, numRows, numColumns);
|
|
134
|
-
return (react_1.default.createElement(CageSumIndicator_1.CageSumIndicator, { key: /* c8 ignore next 1 - defense in depth */ cage.id ?? index, cage: cage, currentSum: currentSum, isComplete: isComplete, isValid: isValid, position: position }));
|
|
135
|
-
})));
|
|
136
|
-
};
|
|
137
|
-
exports.CageOverlay = CageOverlay;
|
|
138
|
-
//# sourceMappingURL=CageOverlay.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"CageOverlay.js","sourceRoot":"","sources":["../../src/components/CageOverlay.tsx"],"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 React, { useMemo, useEffect } from 'react';\nimport { CellId, parseCellId } from '@fgv/ts-sudoku-lib';\nimport { ICageOverlayProps } from '../types';\nimport { CageSumIndicator } from './CageSumIndicator';\nimport { CagePatternManager } from '../utils/CagePatternManager';\nimport { CageLookupManager } from '../utils/CageLookupManager';\nimport { useDiagnosticLogger } from '../contexts/DiagnosticLoggerContext';\nimport { Logging } from '@fgv/ts-utils';\n\n/**\n * Helper to get cell position from CellId\n */\nfunction getTopLeftPosition(\n cellIds: CellId[],\n log: Logging.LogReporter<unknown, unknown>,\n numRows: number,\n numColumns: number\n): { top: number; left: number } {\n if (cellIds.length === 0) return { top: 0, left: 0 };\n\n const positions = cellIds.map((cellId) => {\n const parsed = parseCellId(cellId);\n if (parsed && parsed.row >= 0 && parsed.row < numRows && parsed.col >= 0 && parsed.col < numColumns) {\n return parsed;\n }\n // Fallback - should not happen with valid CellIds\n log.warn(`Invalid CellId format: ${cellId}`);\n return { row: 0, col: 0 };\n });\n\n // Find the top-left most cell\n let minRow = positions[0].row;\n let minCol = positions[0].col;\n\n positions.forEach(({ row, col }) => {\n if (row < minRow || (row === minRow && col < minCol)) {\n minRow = row;\n minCol = col;\n }\n });\n\n // Convert grid coordinates to percentage-based positioning\n const cellWidthPercent = 100 / numColumns;\n const cellHeightPercent = 100 / numRows;\n\n return {\n top: minRow * cellHeightPercent + 0.5, // Small offset from corner\n left: minCol * cellWidthPercent + 0.5 // Small offset from corner\n };\n}\n\n/**\n * Component for rendering cage boundaries and sum indicators in Killer Sudoku\n * @public\n */\nexport const CageOverlay: React.FC<ICageOverlayProps> = ({\n cages,\n gridSize,\n cellSize,\n numRows = 9,\n numColumns = 9,\n className\n}) => {\n const log = useDiagnosticLogger();\n\n // Initialize managers once\n const patternManager = useMemo(() => new CagePatternManager(), []);\n const lookupManager = useMemo(() => CageLookupManager.getInstance(), []);\n\n // Debug logging\n useEffect(() => {\n log.info('CageOverlay rendered', {\n cageCount: cages.length,\n gridSize,\n cellSize,\n cages: cages.map((c) => ({ id: c.cage.id, cellIds: c.cage.cellIds }))\n });\n }, [cages, gridSize, cellSize, log]);\n\n // Calculate SVG viewBox and class names\n const viewBox = `0 0 ${gridSize.width} ${gridSize.height}`;\n\n const overlayClasses = useMemo(() => {\n const classes = ['cage-overlay'];\n if (className) classes.push(className);\n return classes.join(' ');\n }, [className]);\n\n return (\n <div\n className={`\n ${overlayClasses}\n absolute inset-0 w-full h-full pointer-events-none\n `}\n style={{ zIndex: 1000 }}\n data-testid=\"cage-overlay\"\n >\n {/* SVG for cage boundaries using connected border system */}\n <svg\n width=\"100%\"\n height=\"100%\"\n viewBox={viewBox}\n className=\"absolute inset-0\"\n preserveAspectRatio=\"none\"\n >\n {/* Render connected cage borders using two-stage lookup */}\n {cages.map((cageInfo, index) => {\n const { cage, isHighlighted, isValid } = cageInfo;\n\n // Build occupied cells set for this cage\n const occupiedCells = patternManager.buildOccupiedCellsSet(cage.cellIds);\n\n // Render each cell with connected borders\n return cage.cellIds.map((cellId, cellIndex) => {\n // Parse cell position using centralized function\n const parsed = parseCellId(cellId);\n let row = 0,\n col = 0;\n\n if (\n parsed &&\n parsed.row >= 0 &&\n parsed.row < numRows &&\n parsed.col >= 0 &&\n parsed.col < numColumns\n ) {\n row = parsed.row;\n col = parsed.col;\n } else {\n log.warn(`Invalid CellId format: ${cellId}`);\n }\n\n const x = col * cellSize;\n const y = row * cellSize;\n\n // Analyze neighbors and get pattern\n const neighbors = patternManager.analyzeNeighbors(cellId, occupiedCells);\n const neighborPattern = lookupManager.neighborsToPattern(neighbors);\n const pathData = lookupManager.getPatternSVG(neighborPattern);\n\n // Skip if no path data\n if (!pathData) {\n return null;\n }\n\n // Render connected border pattern\n return (\n <g\n key={/* c8 ignore next 1 - defense in depth */ `${cage.id ?? index}-${cellIndex}`}\n transform={`translate(${x}, ${y}) scale(${cellSize})`}\n >\n <path\n d={pathData}\n stroke={isHighlighted ? '#1e40af' : isValid ? '#2563eb' : '#dc2626'}\n strokeWidth={0.8 / cellSize}\n strokeDasharray=\"6 4\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n fill=\"none\"\n opacity=\"1.0\"\n vectorEffect=\"non-scaling-stroke\"\n />\n </g>\n );\n });\n })}\n </svg>\n\n {/* Sum indicators */}\n {cages.map((cageInfo, index) => {\n const { cage, currentSum, isComplete, isValid } = cageInfo;\n const position = getTopLeftPosition(cage.cellIds, log, numRows, numColumns);\n\n return (\n <CageSumIndicator\n key={/* c8 ignore next 1 - defense in depth */ cage.id ?? index}\n cage={cage}\n currentSum={currentSum}\n isComplete={isComplete}\n isValid={isValid}\n position={position}\n />\n );\n })}\n </div>\n );\n};\n"]}
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/*
|
|
3
|
-
* MIT License
|
|
4
|
-
*
|
|
5
|
-
* Copyright (c) 2023 Erik Fortune
|
|
6
|
-
*
|
|
7
|
-
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
8
|
-
* of this software and associated documentation files (the "Software"), to deal
|
|
9
|
-
* in the Software without restriction, including without limitation the rights
|
|
10
|
-
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
11
|
-
* copies of the Software, and to permit persons to whom the Software is
|
|
12
|
-
* furnished to do so, subject to the following conditions:
|
|
13
|
-
*
|
|
14
|
-
* The above copyright notice and this permission notice shall be included in all
|
|
15
|
-
* copies or substantial portions of the Software.
|
|
16
|
-
*
|
|
17
|
-
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
18
|
-
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
19
|
-
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
20
|
-
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
21
|
-
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
22
|
-
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
23
|
-
* SOFTWARE.
|
|
24
|
-
*/
|
|
25
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
-
exports.CageSumIndicator = void 0;
|
|
27
|
-
const tslib_1 = require("tslib");
|
|
28
|
-
const react_1 = tslib_1.__importStar(require("react"));
|
|
29
|
-
/**
|
|
30
|
-
* Component for displaying the target sum of a cage in Killer Sudoku
|
|
31
|
-
* @public
|
|
32
|
-
*/
|
|
33
|
-
const CageSumIndicator = ({ cage, currentSum, isComplete, isValid, position, className }) => {
|
|
34
|
-
// Calculate display classes based on state
|
|
35
|
-
const displayClasses = (0, react_1.useMemo)(() => {
|
|
36
|
-
const classes = ['cage-sum-indicator'];
|
|
37
|
-
/* c8 ignore next 1 - defense in depth */
|
|
38
|
-
if (className)
|
|
39
|
-
classes.push(className);
|
|
40
|
-
if (!isValid) {
|
|
41
|
-
classes.push('cage-sum-indicator--invalid');
|
|
42
|
-
}
|
|
43
|
-
else if (isComplete) {
|
|
44
|
-
classes.push('cage-sum-indicator--complete');
|
|
45
|
-
}
|
|
46
|
-
else if (currentSum !== undefined) {
|
|
47
|
-
classes.push('cage-sum-indicator--partial');
|
|
48
|
-
}
|
|
49
|
-
return classes.join(' ');
|
|
50
|
-
}, [className, isValid, isComplete, currentSum]);
|
|
51
|
-
return (react_1.default.createElement("div", { className: `
|
|
52
|
-
${displayClasses}
|
|
53
|
-
absolute z-20 pointer-events-none
|
|
54
|
-
text-xs font-bold
|
|
55
|
-
bg-white border border-gray-400 rounded px-1
|
|
56
|
-
min-w-[20px] text-center
|
|
57
|
-
shadow-sm
|
|
58
|
-
`, style: {
|
|
59
|
-
top: `${position.top}%`,
|
|
60
|
-
left: `${position.left}%`,
|
|
61
|
-
fontSize: '10px',
|
|
62
|
-
lineHeight: '16px'
|
|
63
|
-
}, "data-testid": `cage-sum-${cage.id}` },
|
|
64
|
-
react_1.default.createElement("div", { className: "text-gray-700" }, cage.total),
|
|
65
|
-
currentSum !== undefined && (react_1.default.createElement("div", { className: `
|
|
66
|
-
text-xs
|
|
67
|
-
${isValid ? 'text-blue-600' : 'text-red-600'}
|
|
68
|
-
`, style: { fontSize: '8px', lineHeight: '10px' } }, currentSum))));
|
|
69
|
-
};
|
|
70
|
-
exports.CageSumIndicator = CageSumIndicator;
|
|
71
|
-
//# sourceMappingURL=CageSumIndicator.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"CageSumIndicator.js","sourceRoot":"","sources":["../../src/components/CageSumIndicator.tsx"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;;;;AAEH,uDAAuC;AAGvC;;;GAGG;AACI,MAAM,gBAAgB,GAAqC,CAAC,EACjE,IAAI,EACJ,UAAU,EACV,UAAU,EACV,OAAO,EACP,QAAQ,EACR,SAAS,EACV,EAAE,EAAE;IACH,2CAA2C;IAC3C,MAAM,cAAc,GAAG,IAAA,eAAO,EAAC,GAAG,EAAE;QAClC,MAAM,OAAO,GAAG,CAAC,oBAAoB,CAAC,CAAC;QAEvC,yCAAyC;QACzC,IAAI,SAAS;YAAE,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAEvC,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC;QAC9C,CAAC;aAAM,IAAI,UAAU,EAAE,CAAC;YACtB,OAAO,CAAC,IAAI,CAAC,8BAA8B,CAAC,CAAC;QAC/C,CAAC;aAAM,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;YACpC,OAAO,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC;QAC9C,CAAC;QAED,OAAO,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC3B,CAAC,EAAE,CAAC,SAAS,EAAE,OAAO,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC;IAEjD,OAAO,CACL,uCACE,SAAS,EAAE;UACP,cAAc;;;;;;OAMjB,EACD,KAAK,EAAE;YACL,GAAG,EAAE,GAAG,QAAQ,CAAC,GAAG,GAAG;YACvB,IAAI,EAAE,GAAG,QAAQ,CAAC,IAAI,GAAG;YACzB,QAAQ,EAAE,MAAM;YAChB,UAAU,EAAE,MAAM;SACnB,iBACY,YAAY,IAAI,CAAC,EAAE,EAAE;QAElC,uCAAK,SAAS,EAAC,eAAe,IAAE,IAAI,CAAC,KAAK,CAAO;QAChD,UAAU,KAAK,SAAS,IAAI,CAC3B,uCACE,SAAS,EAAE;;cAEP,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,cAAc;WAC7C,EACD,KAAK,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,IAE7C,UAAU,CACP,CACP,CACG,CACP,CAAC;AACJ,CAAC,CAAC;AA1DW,QAAA,gBAAgB,oBA0D3B","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 React, { useMemo } from 'react';\nimport { ICageSumIndicatorProps } from '../types';\n\n/**\n * Component for displaying the target sum of a cage in Killer Sudoku\n * @public\n */\nexport const CageSumIndicator: React.FC<ICageSumIndicatorProps> = ({\n cage,\n currentSum,\n isComplete,\n isValid,\n position,\n className\n}) => {\n // Calculate display classes based on state\n const displayClasses = useMemo(() => {\n const classes = ['cage-sum-indicator'];\n\n /* c8 ignore next 1 - defense in depth */\n if (className) classes.push(className);\n\n if (!isValid) {\n classes.push('cage-sum-indicator--invalid');\n } else if (isComplete) {\n classes.push('cage-sum-indicator--complete');\n } else if (currentSum !== undefined) {\n classes.push('cage-sum-indicator--partial');\n }\n\n return classes.join(' ');\n }, [className, isValid, isComplete, currentSum]);\n\n return (\n <div\n className={`\n ${displayClasses}\n absolute z-20 pointer-events-none\n text-xs font-bold\n bg-white border border-gray-400 rounded px-1\n min-w-[20px] text-center\n shadow-sm\n `}\n style={{\n top: `${position.top}%`,\n left: `${position.left}%`,\n fontSize: '10px',\n lineHeight: '16px'\n }}\n data-testid={`cage-sum-${cage.id}`}\n >\n <div className=\"text-gray-700\">{cage.total}</div>\n {currentSum !== undefined && (\n <div\n className={`\n text-xs\n ${isValid ? 'text-blue-600' : 'text-red-600'}\n `}\n style={{ fontSize: '8px', lineHeight: '10px' }}\n >\n {currentSum}\n </div>\n )}\n </div>\n );\n};\n"]}
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/*
|
|
3
|
-
* MIT License
|
|
4
|
-
*
|
|
5
|
-
* Copyright (c) 2023 Erik Fortune
|
|
6
|
-
*
|
|
7
|
-
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
8
|
-
* of this software and associated documentation files (the "Software"), to deal
|
|
9
|
-
* in the Software without restriction, including without limitation the rights
|
|
10
|
-
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
11
|
-
* copies of the Software, and to permit persons to whom the Software is
|
|
12
|
-
* furnished to do so, subject to the following conditions:
|
|
13
|
-
*
|
|
14
|
-
* The above copyright notice and this permission notice shall be included in all
|
|
15
|
-
* copies or substantial portions of the Software.
|
|
16
|
-
*
|
|
17
|
-
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
18
|
-
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
19
|
-
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
20
|
-
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
21
|
-
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
22
|
-
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
23
|
-
* SOFTWARE.
|
|
24
|
-
*/
|
|
25
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
-
exports.CombinationCard = void 0;
|
|
27
|
-
const tslib_1 = require("tslib");
|
|
28
|
-
const react_1 = tslib_1.__importStar(require("react"));
|
|
29
|
-
/**
|
|
30
|
-
* Individual combination card component with toggle functionality
|
|
31
|
-
* @public
|
|
32
|
-
*/
|
|
33
|
-
exports.CombinationCard = react_1.default.memo(({ combination, onToggle, className }) => {
|
|
34
|
-
const { combination: numbers, signature, isEliminated } = combination;
|
|
35
|
-
const handleClick = (0, react_1.useCallback)(() => {
|
|
36
|
-
onToggle(signature);
|
|
37
|
-
}, [onToggle, signature]);
|
|
38
|
-
const handleKeyDown = (0, react_1.useCallback)((event) => {
|
|
39
|
-
if (event.key === ' ' || event.key === 'Enter') {
|
|
40
|
-
event.preventDefault();
|
|
41
|
-
onToggle(signature);
|
|
42
|
-
}
|
|
43
|
-
}, [onToggle, signature]);
|
|
44
|
-
const cardClasses = [
|
|
45
|
-
'px-3 py-1',
|
|
46
|
-
'rounded border',
|
|
47
|
-
'transition-all duration-200',
|
|
48
|
-
'cursor-pointer select-none',
|
|
49
|
-
'hover:shadow-sm',
|
|
50
|
-
'focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-1',
|
|
51
|
-
'inline-flex items-center justify-center',
|
|
52
|
-
isEliminated
|
|
53
|
-
? 'border-gray-300 dark:border-gray-600 bg-gray-50 dark:bg-gray-800 opacity-60'
|
|
54
|
-
: 'border-blue-500 dark:border-blue-400 bg-white dark:bg-gray-800 hover:bg-blue-50 dark:hover:bg-gray-700',
|
|
55
|
-
className
|
|
56
|
-
]
|
|
57
|
-
.filter(Boolean)
|
|
58
|
-
.join(' ');
|
|
59
|
-
const numberDisplay = numbers.join('');
|
|
60
|
-
const ariaLabel = numbers.join(', ');
|
|
61
|
-
return (react_1.default.createElement("button", { type: "button", className: cardClasses, onClick: handleClick, onKeyDown: handleKeyDown, "aria-pressed": isEliminated, "aria-label": `Combination ${ariaLabel}${isEliminated ? ' (eliminated)' : ''}`, role: "button" },
|
|
62
|
-
react_1.default.createElement("span", { className: `text-sm font-mono font-bold ${isEliminated ? 'text-gray-500 dark:text-gray-400 line-through' : 'text-gray-900 dark:text-white'}` }, numberDisplay)));
|
|
63
|
-
}, (prev, next) => {
|
|
64
|
-
return (prev.combination.signature === next.combination.signature &&
|
|
65
|
-
prev.combination.isEliminated === next.combination.isEliminated);
|
|
66
|
-
});
|
|
67
|
-
exports.CombinationCard.displayName = 'CombinationCard';
|
|
68
|
-
//# sourceMappingURL=CombinationCard.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"CombinationCard.js","sourceRoot":"","sources":["../../src/components/CombinationCard.tsx"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;;;;AAEH,uDAA2C;AAG3C;;;GAGG;AACU,QAAA,eAAe,GAC1B,eAAK,CAAC,IAAI,CACR,CAAC,EAAE,WAAW,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,EAAE;IACvC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,GAAG,WAAW,CAAC;IAEtE,MAAM,WAAW,GAAG,IAAA,mBAAW,EAAC,GAAG,EAAE;QACnC,QAAQ,CAAC,SAAS,CAAC,CAAC;IACtB,CAAC,EAAE,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC;IAE1B,MAAM,aAAa,GAAG,IAAA,mBAAW,EAC/B,CAAC,KAA0B,EAAE,EAAE;QAC7B,IAAI,KAAK,CAAC,GAAG,KAAK,GAAG,IAAI,KAAK,CAAC,GAAG,KAAK,OAAO,EAAE,CAAC;YAC/C,KAAK,CAAC,cAAc,EAAE,CAAC;YACvB,QAAQ,CAAC,SAAS,CAAC,CAAC;QACtB,CAAC;IACH,CAAC,EACD,CAAC,QAAQ,EAAE,SAAS,CAAC,CACtB,CAAC;IAEF,MAAM,WAAW,GAAW;QAC1B,WAAW;QACX,gBAAgB;QAChB,6BAA6B;QAC7B,4BAA4B;QAC5B,iBAAiB;QACjB,yEAAyE;QACzE,yCAAyC;QACzC,YAAY;YACV,CAAC,CAAC,6EAA6E;YAC/E,CAAC,CAAC,wGAAwG;QAC5G,SAAS;KACV;SACE,MAAM,CAAC,OAAO,CAAC;SACf,IAAI,CAAC,GAAG,CAAC,CAAC;IAEb,MAAM,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACvC,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAErC,OAAO,CACL,0CACE,IAAI,EAAC,QAAQ,EACb,SAAS,EAAE,WAAW,EACtB,OAAO,EAAE,WAAW,EACpB,SAAS,EAAE,aAAa,kBACV,YAAY,gBACd,eAAe,SAAS,GAAG,YAAY,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,EAAE,EAC5E,IAAI,EAAC,QAAQ;QAEb,wCACE,SAAS,EAAE,+BACT,YAAY,CAAC,CAAC,CAAC,+CAA+C,CAAC,CAAC,CAAC,+BACnE,EAAE,IAED,aAAa,CACT,CACA,CACV,CAAC;AACJ,CAAC,EACD,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE;IACb,OAAO,CACL,IAAI,CAAC,WAAW,CAAC,SAAS,KAAK,IAAI,CAAC,WAAW,CAAC,SAAS;QACzD,IAAI,CAAC,WAAW,CAAC,YAAY,KAAK,IAAI,CAAC,WAAW,CAAC,YAAY,CAChE,CAAC;AACJ,CAAC,CACF,CAAC;AAEJ,uBAAe,CAAC,WAAW,GAAG,iBAAiB,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\nimport React, { useCallback } from 'react';\nimport { ICombinationCardProps } from '../types';\n\n/**\n * Individual combination card component with toggle functionality\n * @public\n */\nexport const CombinationCard: React.NamedExoticComponent<ICombinationCardProps> =\n React.memo<ICombinationCardProps>(\n ({ combination, onToggle, className }) => {\n const { combination: numbers, signature, isEliminated } = combination;\n\n const handleClick = useCallback(() => {\n onToggle(signature);\n }, [onToggle, signature]);\n\n const handleKeyDown = useCallback(\n (event: React.KeyboardEvent) => {\n if (event.key === ' ' || event.key === 'Enter') {\n event.preventDefault();\n onToggle(signature);\n }\n },\n [onToggle, signature]\n );\n\n const cardClasses: string = [\n 'px-3 py-1',\n 'rounded border',\n 'transition-all duration-200',\n 'cursor-pointer select-none',\n 'hover:shadow-sm',\n 'focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-1',\n 'inline-flex items-center justify-center',\n isEliminated\n ? 'border-gray-300 dark:border-gray-600 bg-gray-50 dark:bg-gray-800 opacity-60'\n : 'border-blue-500 dark:border-blue-400 bg-white dark:bg-gray-800 hover:bg-blue-50 dark:hover:bg-gray-700',\n className\n ]\n .filter(Boolean)\n .join(' ');\n\n const numberDisplay = numbers.join('');\n const ariaLabel = numbers.join(', ');\n\n return (\n <button\n type=\"button\"\n className={cardClasses}\n onClick={handleClick}\n onKeyDown={handleKeyDown}\n aria-pressed={isEliminated}\n aria-label={`Combination ${ariaLabel}${isEliminated ? ' (eliminated)' : ''}`}\n role=\"button\"\n >\n <span\n className={`text-sm font-mono font-bold ${\n isEliminated ? 'text-gray-500 dark:text-gray-400 line-through' : 'text-gray-900 dark:text-white'\n }`}\n >\n {numberDisplay}\n </span>\n </button>\n );\n },\n (prev, next) => {\n return (\n prev.combination.signature === next.combination.signature &&\n prev.combination.isEliminated === next.combination.isEliminated\n );\n }\n );\n\nCombinationCard.displayName = 'CombinationCard';\n"]}
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/*
|
|
3
|
-
* MIT License
|
|
4
|
-
*
|
|
5
|
-
* Copyright (c) 2023 Erik Fortune
|
|
6
|
-
*
|
|
7
|
-
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
8
|
-
* of this software and associated documentation files (the "Software"), to deal
|
|
9
|
-
* in the Software without restriction, including without limitation the rights
|
|
10
|
-
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
11
|
-
* copies of the Software, and to permit persons to whom the Software is
|
|
12
|
-
* furnished to do so, subject to the following conditions:
|
|
13
|
-
*
|
|
14
|
-
* The above copyright notice and this permission notice shall be included in all
|
|
15
|
-
* copies or substantial portions of the Software.
|
|
16
|
-
*
|
|
17
|
-
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
18
|
-
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
19
|
-
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
20
|
-
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
21
|
-
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
22
|
-
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
23
|
-
* SOFTWARE.
|
|
24
|
-
*/
|
|
25
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
-
exports.CombinationGrid = void 0;
|
|
27
|
-
const tslib_1 = require("tslib");
|
|
28
|
-
const react_1 = tslib_1.__importDefault(require("react"));
|
|
29
|
-
const CombinationCard_1 = require("./CombinationCard");
|
|
30
|
-
/**
|
|
31
|
-
* Grid layout component for displaying combination cards
|
|
32
|
-
* @public
|
|
33
|
-
*/
|
|
34
|
-
const CombinationGrid = ({ combinations, onToggle, mode, className }) => {
|
|
35
|
-
const gridClasses = ['flex flex-wrap gap-2 p-4', 'overflow-y-auto', 'justify-start', className]
|
|
36
|
-
.filter(Boolean)
|
|
37
|
-
.join(' ');
|
|
38
|
-
if (combinations.length === 0) {
|
|
39
|
-
return (react_1.default.createElement("div", { className: "flex items-center justify-center p-8 text-gray-500 dark:text-gray-400" },
|
|
40
|
-
react_1.default.createElement("p", null, "No combinations available for this cage")));
|
|
41
|
-
}
|
|
42
|
-
// Sort combinations: valid ones first, then by numeric value
|
|
43
|
-
const sortedCombinations = [...combinations].sort((a, b) => {
|
|
44
|
-
// First sort by elimination status (valid ones first)
|
|
45
|
-
if (a.isEliminated !== b.isEliminated) {
|
|
46
|
-
return a.isEliminated ? 1 : -1;
|
|
47
|
-
}
|
|
48
|
-
// Then sort by the numeric value of the combination
|
|
49
|
-
const aValue = parseInt(a.combination.join(''), 10);
|
|
50
|
-
const bValue = parseInt(b.combination.join(''), 10);
|
|
51
|
-
return aValue - bValue;
|
|
52
|
-
});
|
|
53
|
-
return (react_1.default.createElement("div", { className: gridClasses }, sortedCombinations.map((combo) => (react_1.default.createElement(CombinationCard_1.CombinationCard, { key: combo.signature, combination: combo, onToggle: onToggle })))));
|
|
54
|
-
};
|
|
55
|
-
exports.CombinationGrid = CombinationGrid;
|
|
56
|
-
//# sourceMappingURL=CombinationGrid.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"CombinationGrid.js","sourceRoot":"","sources":["../../src/components/CombinationGrid.tsx"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;;;;AAEH,0DAA0B;AAE1B,uDAAoD;AAEpD;;;GAGG;AACI,MAAM,eAAe,GAAoC,CAAC,EAC/D,YAAY,EACZ,QAAQ,EACR,IAAI,EACJ,SAAS,EACV,EAAE,EAAE;IACH,MAAM,WAAW,GAAG,CAAC,0BAA0B,EAAE,iBAAiB,EAAE,eAAe,EAAE,SAAS,CAAC;SAC5F,MAAM,CAAC,OAAO,CAAC;SACf,IAAI,CAAC,GAAG,CAAC,CAAC;IAEb,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9B,OAAO,CACL,uCAAK,SAAS,EAAC,uEAAuE;YACpF,mFAA8C,CAC1C,CACP,CAAC;IACJ,CAAC;IAED,6DAA6D;IAC7D,MAAM,kBAAkB,GAAG,CAAC,GAAG,YAAY,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACzD,sDAAsD;QACtD,IAAI,CAAC,CAAC,YAAY,KAAK,CAAC,CAAC,YAAY,EAAE,CAAC;YACtC,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACjC,CAAC;QACD,oDAAoD;QACpD,MAAM,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;QACpD,MAAM,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;QACpD,OAAO,MAAM,GAAG,MAAM,CAAC;IACzB,CAAC,CAAC,CAAC;IAEH,OAAO,CACL,uCAAK,SAAS,EAAE,WAAW,IACxB,kBAAkB,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CACjC,8BAAC,iCAAe,IAAC,GAAG,EAAE,KAAK,CAAC,SAAS,EAAE,WAAW,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,GAAI,CAClF,CAAC,CACE,CACP,CAAC;AACJ,CAAC,CAAC;AArCW,QAAA,eAAe,mBAqC1B","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 React from 'react';\nimport { ICombinationGridProps } from '../types';\nimport { CombinationCard } from './CombinationCard';\n\n/**\n * Grid layout component for displaying combination cards\n * @public\n */\nexport const CombinationGrid: React.FC<ICombinationGridProps> = ({\n combinations,\n onToggle,\n mode,\n className\n}) => {\n const gridClasses = ['flex flex-wrap gap-2 p-4', 'overflow-y-auto', 'justify-start', className]\n .filter(Boolean)\n .join(' ');\n\n if (combinations.length === 0) {\n return (\n <div className=\"flex items-center justify-center p-8 text-gray-500 dark:text-gray-400\">\n <p>No combinations available for this cage</p>\n </div>\n );\n }\n\n // Sort combinations: valid ones first, then by numeric value\n const sortedCombinations = [...combinations].sort((a, b) => {\n // First sort by elimination status (valid ones first)\n if (a.isEliminated !== b.isEliminated) {\n return a.isEliminated ? 1 : -1;\n }\n // Then sort by the numeric value of the combination\n const aValue = parseInt(a.combination.join(''), 10);\n const bValue = parseInt(b.combination.join(''), 10);\n return aValue - bValue;\n });\n\n return (\n <div className={gridClasses}>\n {sortedCombinations.map((combo) => (\n <CombinationCard key={combo.signature} combination={combo} onToggle={onToggle} />\n ))}\n </div>\n );\n};\n"]}
|