@mideind/netskrafl-react 1.2.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/css/netskrafl.css +44 -14
- package/dist/cjs/index.js +114 -81
- package/dist/cjs/index.js.map +1 -1
- package/dist/esm/css/netskrafl.css +44 -14
- package/dist/esm/index.js +114 -81
- package/dist/esm/index.js.map +1 -1
- package/dist/types.d.ts +2 -2
- package/package.json +1 -1
|
@@ -1001,14 +1001,14 @@ div.netskrafl-tile {
|
|
|
1001
1001
|
cursor: default;
|
|
1002
1002
|
}
|
|
1003
1003
|
|
|
1004
|
-
div.netskrafl-tile.wide,
|
|
1005
|
-
div.netskrafl-tile.extra-wide {
|
|
1004
|
+
div.netskrafl-tile.netskrafl-wide,
|
|
1005
|
+
div.netskrafl-tile.netskrafl-extra-wide {
|
|
1006
1006
|
/* Wide tile in the rack */
|
|
1007
1007
|
text-indent: -6px;
|
|
1008
1008
|
}
|
|
1009
1009
|
|
|
1010
|
-
div.board div.netskrafl-tile.wide,
|
|
1011
|
-
div.board div.netskrafl-tile.extra-wide {
|
|
1010
|
+
div.board div.netskrafl-tile.netskrafl-wide,
|
|
1011
|
+
div.board div.netskrafl-tile.netskrafl-extra-wide {
|
|
1012
1012
|
/* Wide letter tile on the board */
|
|
1013
1013
|
text-indent: -2px;
|
|
1014
1014
|
}
|
|
@@ -1127,8 +1127,8 @@ div.letterscore {
|
|
|
1127
1127
|
vertical-align: bottom;
|
|
1128
1128
|
}
|
|
1129
1129
|
|
|
1130
|
-
div.netskrafl-tile.wide div.letterscore,
|
|
1131
|
-
div.netskrafl-tile.extra-wide div.letterscore {
|
|
1130
|
+
div.netskrafl-tile.netskrafl-wide div.letterscore,
|
|
1131
|
+
div.netskrafl-tile.netskrafl-extra-wide div.letterscore {
|
|
1132
1132
|
/* Adjust letter score tracking for wide tiles */
|
|
1133
1133
|
letter-spacing: -0.5px;
|
|
1134
1134
|
}
|
|
@@ -6408,8 +6408,8 @@ div.netskrafl-container input[type="checkbox"] {
|
|
|
6408
6408
|
padding-top: 2px;
|
|
6409
6409
|
margin-left: 0;
|
|
6410
6410
|
}
|
|
6411
|
-
div.netskrafl-tile.wide,
|
|
6412
|
-
div.netskrafl-tile.extra-wide {
|
|
6411
|
+
div.netskrafl-tile.netskrafl-wide,
|
|
6412
|
+
div.netskrafl-tile.netskrafl-extra-wide {
|
|
6413
6413
|
/* Wide character tile */
|
|
6414
6414
|
text-indent: -6px;
|
|
6415
6415
|
margin-left: 0;
|
|
@@ -6419,8 +6419,8 @@ div.netskrafl-container input[type="checkbox"] {
|
|
|
6419
6419
|
/* Extra wide character tile: Q */
|
|
6420
6420
|
text-indent: -8px;
|
|
6421
6421
|
}
|
|
6422
|
-
div.board div.netskrafl-tile.wide,
|
|
6423
|
-
div.board div.netskrafl-tile.extra-wide {
|
|
6422
|
+
div.board div.netskrafl-tile.netskrafl-wide,
|
|
6423
|
+
div.board div.netskrafl-tile.netskrafl-extra-wide {
|
|
6424
6424
|
/* Wide letter tile on the board */
|
|
6425
6425
|
text-indent: -3.5px;
|
|
6426
6426
|
}
|
|
@@ -6444,14 +6444,14 @@ div.netskrafl-container input[type="checkbox"] {
|
|
|
6444
6444
|
right: 0px;
|
|
6445
6445
|
bottom: 1px;
|
|
6446
6446
|
}
|
|
6447
|
-
div.netskrafl-tile.wide div.letterscore,
|
|
6448
|
-
div.netskrafl-tile.extra-wide div.letterscore {
|
|
6447
|
+
div.netskrafl-tile.netskrafl-wide div.letterscore,
|
|
6448
|
+
div.netskrafl-tile.netskrafl-extra-wide div.letterscore {
|
|
6449
6449
|
/* Adjust letter score position for wide tiles */
|
|
6450
6450
|
right: 1px;
|
|
6451
6451
|
left: auto;
|
|
6452
6452
|
}
|
|
6453
|
-
.rack div.netskrafl-tile.wide div.letterscore,
|
|
6454
|
-
.rack div.netskrafl-tile.extra-wide div.letterscore {
|
|
6453
|
+
.rack div.netskrafl-tile.netskrafl-wide div.letterscore,
|
|
6454
|
+
.rack div.netskrafl-tile.netskrafl-extra-wide div.letterscore {
|
|
6455
6455
|
/* Adjust letter score position for wide tiles */
|
|
6456
6456
|
right: 1px;
|
|
6457
6457
|
left: auto;
|
|
@@ -8574,6 +8574,14 @@ div.gatadagsins-board-area.celebrate div.netskrafl-tile.netskrafl-racktile{
|
|
|
8574
8574
|
background-color: #f8f8f8;
|
|
8575
8575
|
}
|
|
8576
8576
|
|
|
8577
|
+
.netskrafl-container .tab-icon-wrapper {
|
|
8578
|
+
position: relative;
|
|
8579
|
+
line-height: 1;
|
|
8580
|
+
display: flex;
|
|
8581
|
+
align-items: center;
|
|
8582
|
+
justify-content: center;
|
|
8583
|
+
}
|
|
8584
|
+
|
|
8577
8585
|
.netskrafl-container .tab-icon {
|
|
8578
8586
|
line-height: 1;
|
|
8579
8587
|
display: flex;
|
|
@@ -8585,6 +8593,20 @@ div.gatadagsins-board-area.celebrate div.netskrafl-tile.netskrafl-racktile{
|
|
|
8585
8593
|
font-size: 22px;
|
|
8586
8594
|
}
|
|
8587
8595
|
|
|
8596
|
+
.netskrafl-container .tab-badge {
|
|
8597
|
+
position: absolute;
|
|
8598
|
+
top: -4px;
|
|
8599
|
+
right: -12px;
|
|
8600
|
+
font-size: 13px;
|
|
8601
|
+
color: var(--malfridur-secondary);
|
|
8602
|
+
line-height: 1;
|
|
8603
|
+
pointer-events: none;
|
|
8604
|
+
}
|
|
8605
|
+
|
|
8606
|
+
.netskrafl-container .tab-badge .glyphicon {
|
|
8607
|
+
font-size: 13px;
|
|
8608
|
+
}
|
|
8609
|
+
|
|
8588
8610
|
.netskrafl-container .tab-label {
|
|
8589
8611
|
margin-top: 2px;
|
|
8590
8612
|
font-size: 12px;
|
|
@@ -8967,6 +8989,14 @@ div.gatadagsins-board-area.celebrate div.netskrafl-tile.netskrafl-racktile{
|
|
|
8967
8989
|
font-size: 24px;
|
|
8968
8990
|
}
|
|
8969
8991
|
|
|
8992
|
+
.netskrafl-container .tab-badge {
|
|
8993
|
+
font-size: 14px;
|
|
8994
|
+
}
|
|
8995
|
+
|
|
8996
|
+
.netskrafl-container .tab-badge .glyphicon {
|
|
8997
|
+
font-size: 14px;
|
|
8998
|
+
}
|
|
8999
|
+
|
|
8970
9000
|
.netskrafl-container .tab-label {
|
|
8971
9001
|
margin-top: 2px;
|
|
8972
9002
|
font-size: 12px;
|
package/dist/cjs/index.js
CHANGED
|
@@ -23,8 +23,8 @@ const saveAuthSettings = (settings) => {
|
|
|
23
23
|
filteredSettings.userId = settings.userId;
|
|
24
24
|
if (settings.userNick !== undefined)
|
|
25
25
|
filteredSettings.userNick = settings.userNick;
|
|
26
|
-
if (settings.
|
|
27
|
-
filteredSettings.
|
|
26
|
+
if (settings.firebaseApiKey !== undefined)
|
|
27
|
+
filteredSettings.firebaseApiKey = settings.firebaseApiKey;
|
|
28
28
|
if (settings.beginner !== undefined)
|
|
29
29
|
filteredSettings.beginner = settings.beginner;
|
|
30
30
|
if (settings.fairPlay !== undefined)
|
|
@@ -90,7 +90,7 @@ const applyPersistedSettings = (state) => {
|
|
|
90
90
|
account: state.account || persisted.account || state.userId, // Use userId as fallback
|
|
91
91
|
userId: state.userId || persisted.userId || state.userId,
|
|
92
92
|
userNick: state.userNick || persisted.userNick || state.userNick,
|
|
93
|
-
|
|
93
|
+
firebaseApiKey: state.firebaseApiKey || persisted.firebaseApiKey || state.firebaseApiKey,
|
|
94
94
|
beginner: (_a = persisted.beginner) !== null && _a !== void 0 ? _a : state.beginner,
|
|
95
95
|
fairPlay: (_b = persisted.fairPlay) !== null && _b !== void 0 ? _b : state.fairPlay,
|
|
96
96
|
ready: (_c = persisted.ready) !== null && _c !== void 0 ? _c : state.ready,
|
|
@@ -100,8 +100,8 @@ const applyPersistedSettings = (state) => {
|
|
|
100
100
|
|
|
101
101
|
const DEFAULT_STATE = {
|
|
102
102
|
projectId: "netskrafl",
|
|
103
|
-
|
|
104
|
-
|
|
103
|
+
firebaseApiKey: "",
|
|
104
|
+
databaseUrl: "",
|
|
105
105
|
firebaseSenderId: "",
|
|
106
106
|
firebaseAppId: "",
|
|
107
107
|
measurementId: "",
|
|
@@ -27215,12 +27215,12 @@ let database;
|
|
|
27215
27215
|
let analytics;
|
|
27216
27216
|
function initFirebase(state) {
|
|
27217
27217
|
try {
|
|
27218
|
-
const { projectId,
|
|
27218
|
+
const { projectId, firebaseApiKey, databaseUrl, firebaseSenderId, firebaseAppId, measurementId } = state;
|
|
27219
27219
|
const firebaseOptions = {
|
|
27220
27220
|
projectId,
|
|
27221
|
-
apiKey:
|
|
27221
|
+
apiKey: firebaseApiKey,
|
|
27222
27222
|
authDomain: `${projectId}.firebaseapp.com`,
|
|
27223
|
-
databaseURL,
|
|
27223
|
+
databaseURL: databaseUrl,
|
|
27224
27224
|
storageBucket: `${projectId}.firebasestorage.app`,
|
|
27225
27225
|
messagingSenderId: firebaseSenderId,
|
|
27226
27226
|
appId: firebaseAppId,
|
|
@@ -27651,7 +27651,7 @@ const ensureAuthenticated = async (state) => {
|
|
|
27651
27651
|
// Update the user's nickname
|
|
27652
27652
|
state.userNick = result.nickname || state.userNick;
|
|
27653
27653
|
// Use the server's Firebase API key, if provided
|
|
27654
|
-
state.
|
|
27654
|
+
state.firebaseApiKey = result.firebase_api_key || state.firebaseApiKey;
|
|
27655
27655
|
// Load state flags and preferences
|
|
27656
27656
|
state.beginner = (_b = (_a = result.prefs) === null || _a === void 0 ? void 0 : _a.beginner) !== null && _b !== void 0 ? _b : true;
|
|
27657
27657
|
state.fairPlay = (_d = (_c = result.prefs) === null || _c === void 0 ? void 0 : _c.fairplay) !== null && _d !== void 0 ? _d : false;
|
|
@@ -27662,7 +27662,7 @@ const ensureAuthenticated = async (state) => {
|
|
|
27662
27662
|
userEmail: state.userEmail, // CRITICAL: Include email to validate ownership
|
|
27663
27663
|
userId: state.userId,
|
|
27664
27664
|
userNick: state.userNick,
|
|
27665
|
-
|
|
27665
|
+
firebaseApiKey: state.firebaseApiKey,
|
|
27666
27666
|
beginner: state.beginner,
|
|
27667
27667
|
fairPlay: state.fairPlay,
|
|
27668
27668
|
ready: state.ready,
|
|
@@ -30892,14 +30892,16 @@ const Board = (initialVnode) => {
|
|
|
30892
30892
|
}
|
|
30893
30893
|
return {
|
|
30894
30894
|
view: (vnode) => {
|
|
30895
|
-
const scale = view.boardScale || 1.0;
|
|
30895
|
+
// const scale = view.boardScale || 1.0;
|
|
30896
30896
|
let attrs = {};
|
|
30897
30897
|
// Add handlers for pinch zoom functionality
|
|
30898
30898
|
// Note: resist the temptation to pass zoomIn/zoomOut directly,
|
|
30899
30899
|
// as that would not bind the 'this' pointer correctly
|
|
30900
30900
|
addPinchZoom(attrs, () => view.zoomIn(), () => view.zoomOut());
|
|
30901
|
+
/*
|
|
30901
30902
|
if (scale !== 1.0)
|
|
30902
|
-
|
|
30903
|
+
attrs.style = `transform: scale(${scale})`;
|
|
30904
|
+
*/
|
|
30903
30905
|
return m(".board", { id: "board-parent" }, m("table.board", attrs, m("tbody", allrows())));
|
|
30904
30906
|
}
|
|
30905
30907
|
};
|
|
@@ -31668,12 +31670,12 @@ const RightColumn = (initialVnode) => {
|
|
|
31668
31670
|
*/
|
|
31669
31671
|
const Beginner = (initialVnode) => {
|
|
31670
31672
|
// Show the board color guide
|
|
31671
|
-
const { view } = initialVnode.attrs;
|
|
31673
|
+
const { view, showClose } = initialVnode.attrs;
|
|
31672
31674
|
const { model, actions } = view;
|
|
31673
31675
|
const state = model.state;
|
|
31674
31676
|
return {
|
|
31675
31677
|
view: () => m(".board-help", { title: ts("Hvernig reitirnir margfalda stigin") }, [
|
|
31676
|
-
m(".board-help-close", {
|
|
31678
|
+
showClose ? m(".board-help-close", {
|
|
31677
31679
|
title: ts("Loka þessari hjálp"),
|
|
31678
31680
|
onclick: (ev) => {
|
|
31679
31681
|
// Close the guide and set a preference not to see it again
|
|
@@ -31683,7 +31685,7 @@ const Beginner = (initialVnode) => {
|
|
|
31683
31685
|
}
|
|
31684
31686
|
ev.preventDefault();
|
|
31685
31687
|
}
|
|
31686
|
-
}, glyph("remove")),
|
|
31688
|
+
}, glyph("remove")) : null,
|
|
31687
31689
|
m(".board-colors", [
|
|
31688
31690
|
m(".board-color[id='triple-word']", ["3 x", m("br"), t("orð")]),
|
|
31689
31691
|
m(".board-color[id='double-word']", ["2 x", m("br"), t("orð")]),
|
|
@@ -31739,7 +31741,7 @@ const GameView = {
|
|
|
31739
31741
|
// These elements appear after the game container, since we want
|
|
31740
31742
|
// them to be above it in the z-order
|
|
31741
31743
|
m(LeftLogo),
|
|
31742
|
-
(state === null || state === void 0 ? void 0 : state.beginner) ? m(Beginner, { view }) : "",
|
|
31744
|
+
(state === null || state === void 0 ? void 0 : state.beginner) ? m(Beginner, { view, showClose: true }) : "",
|
|
31743
31745
|
m(Info),
|
|
31744
31746
|
]);
|
|
31745
31747
|
}
|
|
@@ -32266,6 +32268,8 @@ class View {
|
|
|
32266
32268
|
this.dialogStack = [];
|
|
32267
32269
|
// The current scaling of the board
|
|
32268
32270
|
this.boardScale = 1.0;
|
|
32271
|
+
// Pending zoom animation timeout (for cancellation across rapid zoom operations)
|
|
32272
|
+
this.zoomAnimationTimeout = null;
|
|
32269
32273
|
this.selectedTab = "movelist";
|
|
32270
32274
|
this.actions = actions;
|
|
32271
32275
|
// Initialize media listeners now that we have the view reference
|
|
@@ -32409,13 +32413,17 @@ class View {
|
|
|
32409
32413
|
}
|
|
32410
32414
|
resetScale() {
|
|
32411
32415
|
// Reset the board scale (zoom) to 100% and the scroll origin to (0, 0)
|
|
32416
|
+
// Cancel any pending zoom animation
|
|
32417
|
+
if (this.zoomAnimationTimeout !== null) {
|
|
32418
|
+
clearTimeout(this.zoomAnimationTimeout);
|
|
32419
|
+
this.zoomAnimationTimeout = null;
|
|
32420
|
+
}
|
|
32412
32421
|
this.boardScale = 1.0;
|
|
32413
32422
|
const boardParent = document.getElementById("board-parent");
|
|
32414
32423
|
const board = boardParent === null || boardParent === void 0 ? void 0 : boardParent.children[0];
|
|
32415
32424
|
if (board) {
|
|
32416
32425
|
board.style.transition = 'none';
|
|
32417
|
-
board.style.
|
|
32418
|
-
board.style.transform = `translate(0, 0) scale(1.0)`;
|
|
32426
|
+
board.style.transform = `translate(0px, 0px) scale(1)`;
|
|
32419
32427
|
}
|
|
32420
32428
|
if (boardParent)
|
|
32421
32429
|
boardParent.scrollTo(0, 0);
|
|
@@ -32426,7 +32434,7 @@ class View {
|
|
|
32426
32434
|
// Use either the regular game or the riddle (Gáta Dagsins)
|
|
32427
32435
|
const game = model.game || model.riddle;
|
|
32428
32436
|
// Update the board scale (zoom)
|
|
32429
|
-
|
|
32437
|
+
const scrollIntoView = (sq) => {
|
|
32430
32438
|
// Scroll a square above and to the left of the placed tile into view,
|
|
32431
32439
|
// with a smooth concurrent zoom and pan animation,
|
|
32432
32440
|
// taking clamping into account to ensure that the board always fills
|
|
@@ -32440,9 +32448,7 @@ class View {
|
|
|
32440
32448
|
const row = Math.max(0, vec.row - offset);
|
|
32441
32449
|
const col = Math.max(0, vec.col - offset);
|
|
32442
32450
|
const c = coord(row, col);
|
|
32443
|
-
//
|
|
32444
|
-
board.style.transformOrigin = 'top left';
|
|
32445
|
-
board.style.transform = `translate(0, 0) scale(1.0)`;
|
|
32451
|
+
// board.style.transform = `translate(0, 0) scale(1.0)`;
|
|
32446
32452
|
const el = document.getElementById("sq_" + c);
|
|
32447
32453
|
const elRect = el === null || el === void 0 ? void 0 : el.getBoundingClientRect();
|
|
32448
32454
|
const boardRect = boardParent.getBoundingClientRect();
|
|
@@ -32460,22 +32466,32 @@ class View {
|
|
|
32460
32466
|
// the top left corner of the viewport, or as close as possible
|
|
32461
32467
|
let targetScrollLeft = Math.min(elRect.left - boardRect.left, maxScrollLeft);
|
|
32462
32468
|
let targetScrollTop = Math.min(elRect.top - boardRect.top, maxScrollTop);
|
|
32469
|
+
// Cancel any pending zoom animation
|
|
32470
|
+
if (this.zoomAnimationTimeout !== null) {
|
|
32471
|
+
clearTimeout(this.zoomAnimationTimeout);
|
|
32472
|
+
this.zoomAnimationTimeout = null;
|
|
32473
|
+
}
|
|
32463
32474
|
// Now animate both translate (for pan) and scale (for zoom) concurrently
|
|
32464
|
-
board.style.transition = 'transform 0.3s ease-in-out';
|
|
32465
32475
|
// Note: transforms are applied right to left
|
|
32466
|
-
board.style.transform
|
|
32467
|
-
|
|
32468
|
-
|
|
32469
|
-
board.
|
|
32470
|
-
|
|
32476
|
+
board.style.transition = 'transform 0.3s ease-in-out';
|
|
32477
|
+
const translateValue = `${-targetScrollLeft}px, ${-targetScrollTop}px`;
|
|
32478
|
+
const scaleValue = `${ZOOM_FACTOR}`;
|
|
32479
|
+
board.style.transform = `translate(${translateValue}) scale(${scaleValue})`;
|
|
32480
|
+
// Use setTimeout as primary mechanism (transitionend is unreliable in Firefox/Safari)
|
|
32481
|
+
// 350ms = 300ms animation + 50ms buffer
|
|
32482
|
+
this.zoomAnimationTimeout = setTimeout(() => {
|
|
32483
|
+
this.zoomAnimationTimeout = null;
|
|
32484
|
+
// Reset the transform (remove translate, keep scale)
|
|
32471
32485
|
board.style.transition = 'none';
|
|
32472
|
-
board.style.transform = `translate(
|
|
32473
|
-
//
|
|
32474
|
-
|
|
32475
|
-
|
|
32476
|
-
|
|
32486
|
+
board.style.transform = `translate(0px, 0px) scale(${ZOOM_FACTOR})`;
|
|
32487
|
+
// Wait for the browser to process the style changes before setting scroll position
|
|
32488
|
+
// This is needed for Firefox and Safari to properly update the scroll position
|
|
32489
|
+
requestAnimationFrame(() => {
|
|
32490
|
+
boardParent.scrollTo(targetScrollLeft, targetScrollTop);
|
|
32491
|
+
});
|
|
32492
|
+
}, 350);
|
|
32477
32493
|
}
|
|
32478
|
-
}
|
|
32494
|
+
};
|
|
32479
32495
|
if (!game || ((_a = model.state) === null || _a === void 0 ? void 0 : _a.uiFullscreen) || game.moveInProgress) {
|
|
32480
32496
|
// No game or we're in full screen mode: always 100% scale
|
|
32481
32497
|
// Also, as soon as a move is being processed by the server, we zoom out
|
|
@@ -36612,27 +36628,25 @@ const SunCorona = {
|
|
|
36612
36628
|
const MobileStatus = () => {
|
|
36613
36629
|
return {
|
|
36614
36630
|
view: (vnode) => {
|
|
36615
|
-
const {
|
|
36616
|
-
const {
|
|
36631
|
+
const { view, selectedMoves, bestMove, onMoveClick, onStatsClick } = vnode.attrs;
|
|
36632
|
+
const { riddle } = view.model;
|
|
36633
|
+
if (!riddle)
|
|
36634
|
+
return null;
|
|
36635
|
+
const { bestPossibleScore, globalBestScore, personalBestScore } = riddle;
|
|
36617
36636
|
// Determine if player achieved best possible score
|
|
36618
36637
|
const achieved = bestMove !== undefined;
|
|
36619
36638
|
const celebrate = bestMove && bestMove.word !== "";
|
|
36620
|
-
// Get player's current best score
|
|
36621
|
-
// If the player achieved the best possible score, it's in bestMove (not selectedMoves)
|
|
36622
|
-
const playerBestScore = (bestMove && bestMove.word !== "")
|
|
36623
|
-
? bestMove.score
|
|
36624
|
-
: (selectedMoves.length > 0 ? selectedMoves[0].score : 0);
|
|
36625
36639
|
// Determine current leader score (may be this player or another)
|
|
36626
36640
|
let leaderScore = 0;
|
|
36627
36641
|
let isPlayerLeading = false;
|
|
36628
36642
|
if (globalBestScore && globalBestScore.score > 0) {
|
|
36629
36643
|
leaderScore = globalBestScore.score;
|
|
36630
36644
|
// Check if player is leading
|
|
36631
|
-
isPlayerLeading =
|
|
36645
|
+
isPlayerLeading = personalBestScore >= globalBestScore.score;
|
|
36632
36646
|
}
|
|
36633
36647
|
else {
|
|
36634
|
-
leaderScore =
|
|
36635
|
-
isPlayerLeading =
|
|
36648
|
+
leaderScore = personalBestScore;
|
|
36649
|
+
isPlayerLeading = personalBestScore > 0;
|
|
36636
36650
|
}
|
|
36637
36651
|
return m(".mobile-status-container", [
|
|
36638
36652
|
// Current word score (leftmost) - uses RiddleScore component in mobile mode
|
|
@@ -36644,12 +36658,12 @@ const MobileStatus = () => {
|
|
|
36644
36658
|
}, [
|
|
36645
36659
|
// Player's best score
|
|
36646
36660
|
m(".mobile-status-card-item.player-best", [
|
|
36647
|
-
m(".mobile-status-label", ts("Þín besta
|
|
36648
|
-
m(".mobile-status-score",
|
|
36661
|
+
m(".mobile-status-label", ts("Þín besta")),
|
|
36662
|
+
m(".mobile-status-score", personalBestScore.toString())
|
|
36649
36663
|
]),
|
|
36650
36664
|
// Current leader score
|
|
36651
36665
|
m(".mobile-status-card-item.leader" + (isPlayerLeading ? ".is-player" : ""), [
|
|
36652
|
-
m(".mobile-status-label", isPlayerLeading ? ts("Þú leiðir!") : ts("
|
|
36666
|
+
m(".mobile-status-label", isPlayerLeading ? ts("Þú leiðir!") : ts("Best til þessa")),
|
|
36653
36667
|
m(".mobile-status-score", leaderScore.toString())
|
|
36654
36668
|
]),
|
|
36655
36669
|
// Chevron indicator (overlaid at bottom center)
|
|
@@ -36693,8 +36707,11 @@ const TabBar = {
|
|
|
36693
36707
|
key: tab.id,
|
|
36694
36708
|
onclick: () => onTabChange(tab.id)
|
|
36695
36709
|
}, [
|
|
36696
|
-
|
|
36697
|
-
tab.
|
|
36710
|
+
m("span.tab-icon-wrapper", [
|
|
36711
|
+
tab.iconGlyph ? m("span.tab-icon", glyph(tab.iconGlyph)) :
|
|
36712
|
+
tab.icon ? m("span.tab-icon", tab.icon) : null,
|
|
36713
|
+
tab.badgeGlyph ? m("span.tab-badge", glyph(tab.badgeGlyph)) : null
|
|
36714
|
+
]),
|
|
36698
36715
|
m("span.tab-label", tab.label)
|
|
36699
36716
|
])));
|
|
36700
36717
|
}
|
|
@@ -36783,7 +36800,7 @@ const BestPossibleScore = () => {
|
|
|
36783
36800
|
}
|
|
36784
36801
|
else {
|
|
36785
36802
|
// Someone else achieved it - indicate this
|
|
36786
|
-
topLabel = ts("
|
|
36803
|
+
topLabel = ts("Besta mögulega lögn\n(er þegar fundin)");
|
|
36787
36804
|
}
|
|
36788
36805
|
}
|
|
36789
36806
|
else {
|
|
@@ -36833,7 +36850,7 @@ const PlayerMovesOverlay = () => {
|
|
|
36833
36850
|
const allMoveElements = [];
|
|
36834
36851
|
function scoreDetails(move) {
|
|
36835
36852
|
if (move.isGlobalBestScore) {
|
|
36836
|
-
if (move.word === ""
|
|
36853
|
+
if (move.word === "") {
|
|
36837
36854
|
// Another player holds the top score
|
|
36838
36855
|
return [m(GlobalBestScore, { thisPlayer: false, score: move.score })];
|
|
36839
36856
|
}
|
|
@@ -37063,18 +37080,26 @@ const LeaderboardView = {
|
|
|
37063
37080
|
const RightSideTabs = () => {
|
|
37064
37081
|
// Component-local state for active tab (defaults to performance)
|
|
37065
37082
|
let activeTab = "performance";
|
|
37066
|
-
const tabs = [
|
|
37067
|
-
{ id: "performance", label: ts("Frammistaða"), iconGlyph: "dashboard" },
|
|
37068
|
-
{ id: "stats", label: ts("Tölfræði"), iconGlyph: "stats" },
|
|
37069
|
-
{ id: "leaderboard", label: ts("Stigatafla"), iconGlyph: "tower" }
|
|
37070
|
-
];
|
|
37071
37083
|
return {
|
|
37072
37084
|
view: (vnode) => {
|
|
37073
37085
|
const { view, selectedMoves, bestMove, onMoveClick } = vnode.attrs;
|
|
37074
|
-
const { riddle, state } = view.model;
|
|
37086
|
+
const { riddle, state, leaderboard } = view.model;
|
|
37075
37087
|
if (!riddle) {
|
|
37076
37088
|
return m(".gatadagsins-right-side-tabs", "");
|
|
37077
37089
|
}
|
|
37090
|
+
// Check if current user is on the leaderboard
|
|
37091
|
+
const currentUserId = (state === null || state === void 0 ? void 0 : state.userId) || "";
|
|
37092
|
+
const isOnLeaderboard = leaderboard && leaderboard.some(entry => entry.userId === currentUserId);
|
|
37093
|
+
const tabs = [
|
|
37094
|
+
{ id: "performance", label: ts("Frammistaða"), iconGlyph: "dashboard" },
|
|
37095
|
+
{ id: "stats", label: ts("Tölfræði"), iconGlyph: "stats" },
|
|
37096
|
+
{
|
|
37097
|
+
id: "leaderboard",
|
|
37098
|
+
label: ts("Stigatafla"),
|
|
37099
|
+
iconGlyph: "tower",
|
|
37100
|
+
badgeGlyph: isOnLeaderboard ? "star" : undefined
|
|
37101
|
+
}
|
|
37102
|
+
];
|
|
37078
37103
|
const handleTabChange = (tabId) => {
|
|
37079
37104
|
activeTab = tabId;
|
|
37080
37105
|
};
|
|
@@ -37140,7 +37165,7 @@ const GataDagsinsRightSide = {
|
|
|
37140
37165
|
return m(".gatadagsins-right-side-wrapper", riddle ? [
|
|
37141
37166
|
// Mobile-only status bar (visible on mobile, hidden on desktop)
|
|
37142
37167
|
m(".gatadagsins-mobile-status", m(MobileStatus, {
|
|
37143
|
-
|
|
37168
|
+
view,
|
|
37144
37169
|
selectedMoves,
|
|
37145
37170
|
bestMove,
|
|
37146
37171
|
onMoveClick: handleMoveClick,
|
|
@@ -37260,31 +37285,39 @@ const GataDagsinsHelp = {
|
|
|
37260
37285
|
const StatsModal = () => {
|
|
37261
37286
|
// Component-local state for active tab (defaults to stats)
|
|
37262
37287
|
let activeTab = "stats";
|
|
37263
|
-
const tabs = [
|
|
37264
|
-
{ id: "stats", label: ts("Tölfræði"), iconGlyph: "stats" },
|
|
37265
|
-
{ id: "leaderboard", label: ts("Stigatafla"), iconGlyph: "tower" }
|
|
37266
|
-
];
|
|
37267
37288
|
return {
|
|
37268
37289
|
view: (vnode) => {
|
|
37269
37290
|
const { view, onClose } = vnode.attrs;
|
|
37270
|
-
const { riddle, state } = view.model;
|
|
37291
|
+
const { riddle, state, leaderboard } = view.model;
|
|
37271
37292
|
if (!riddle) {
|
|
37272
37293
|
return null;
|
|
37273
37294
|
}
|
|
37295
|
+
// Check if current user is on the leaderboard
|
|
37296
|
+
const currentUserId = (state === null || state === void 0 ? void 0 : state.userId) || "";
|
|
37297
|
+
const isOnLeaderboard = leaderboard && leaderboard.some(entry => entry.userId === currentUserId);
|
|
37298
|
+
const tabs = [
|
|
37299
|
+
{ id: "stats", label: ts("Tölfræði"), iconGlyph: "stats" },
|
|
37300
|
+
{
|
|
37301
|
+
id: "leaderboard",
|
|
37302
|
+
label: ts("Stigatafla"),
|
|
37303
|
+
iconGlyph: "tower",
|
|
37304
|
+
badgeGlyph: isOnLeaderboard ? "star" : undefined
|
|
37305
|
+
}
|
|
37306
|
+
];
|
|
37274
37307
|
const handleTabChange = (tabId) => {
|
|
37275
37308
|
activeTab = tabId;
|
|
37276
37309
|
};
|
|
37277
37310
|
return [
|
|
37278
37311
|
// Backdrop
|
|
37279
37312
|
m(".modal-backdrop-netskrafl", {
|
|
37280
|
-
onclick: (e) => { e.preventDefault(); },
|
|
37281
|
-
onwheel: (e) => { e.preventDefault(); e.stopPropagation(); },
|
|
37282
|
-
ontouchmove: (e) => { e.preventDefault(); e.stopPropagation(); }
|
|
37313
|
+
onclick: (e) => { e.preventDefault(); return false; },
|
|
37314
|
+
onwheel: (e) => { e.preventDefault(); e.stopPropagation(); return false; },
|
|
37315
|
+
ontouchmove: (e) => { e.preventDefault(); e.stopPropagation(); return false; }
|
|
37283
37316
|
}),
|
|
37284
37317
|
// Modal dialog
|
|
37285
37318
|
m(".modal-dialog.stats-modal", {
|
|
37286
|
-
onwheel: (e) => { e.stopPropagation(); },
|
|
37287
|
-
ontouchmove: (e) => { e.stopPropagation(); }
|
|
37319
|
+
onwheel: (e) => { e.stopPropagation(); return false; },
|
|
37320
|
+
ontouchmove: (e) => { e.stopPropagation(); return false; }
|
|
37288
37321
|
}, [
|
|
37289
37322
|
m(".modal-content", [
|
|
37290
37323
|
// Close button in top right
|
|
@@ -37417,9 +37450,9 @@ const GataDagsins$1 = () => {
|
|
|
37417
37450
|
id: "gatadagsins-background",
|
|
37418
37451
|
}, [
|
|
37419
37452
|
// The main content area
|
|
37420
|
-
|
|
37453
|
+
m(".gatadagsins-container", [
|
|
37421
37454
|
// Main display area with flex layout
|
|
37422
|
-
m(".gatadagsins-main", [
|
|
37455
|
+
riddle ? m(".gatadagsins-main", [
|
|
37423
37456
|
// Board and rack component (left side)
|
|
37424
37457
|
m(GataDagsinsBoardAndRack, { view }),
|
|
37425
37458
|
// Right-side component with scores and comparisons
|
|
@@ -37428,17 +37461,17 @@ const GataDagsins$1 = () => {
|
|
|
37428
37461
|
riddle.askingForBlank
|
|
37429
37462
|
? m(BlankDialog, { game: riddle })
|
|
37430
37463
|
: "",
|
|
37431
|
-
])
|
|
37432
|
-
|
|
37433
|
-
|
|
37434
|
-
|
|
37435
|
-
|
|
37436
|
-
|
|
37437
|
-
|
|
37438
|
-
|
|
37439
|
-
|
|
37440
|
-
|
|
37441
|
-
|
|
37464
|
+
]) : "",
|
|
37465
|
+
// The left margin elements: back button and info/help button
|
|
37466
|
+
// These elements appear after the main container for proper z-order
|
|
37467
|
+
// m(LeftLogo), // Currently no need for the logo for Gáta Dagsins
|
|
37468
|
+
// Show the Beginner component if the user is a beginner
|
|
37469
|
+
((_a = model.state) === null || _a === void 0 ? void 0 : _a.beginner) ? m(Beginner, { view, showClose: false }) : "",
|
|
37470
|
+
// Custom Info button for GataDagsins that shows help dialog
|
|
37471
|
+
m(".info", { title: ts("Upplýsingar og hjálp") }, m("a.iconlink", { href: "#", onclick: (e) => { e.preventDefault(); toggleHelp(); } }, glyph("info-sign"))),
|
|
37472
|
+
// Help dialog and backdrop
|
|
37473
|
+
showHelp ? m(GataDagsinsHelp, { onClose: toggleHelp }) : "",
|
|
37474
|
+
]),
|
|
37442
37475
|
// Stats modal and backdrop (mobile only)
|
|
37443
37476
|
showStatsModal ? m(StatsModal, { view, onClose: toggleStatsModal }) : "",
|
|
37444
37477
|
]);
|