@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/esm/index.js
CHANGED
|
@@ -21,8 +21,8 @@ const saveAuthSettings = (settings) => {
|
|
|
21
21
|
filteredSettings.userId = settings.userId;
|
|
22
22
|
if (settings.userNick !== undefined)
|
|
23
23
|
filteredSettings.userNick = settings.userNick;
|
|
24
|
-
if (settings.
|
|
25
|
-
filteredSettings.
|
|
24
|
+
if (settings.firebaseApiKey !== undefined)
|
|
25
|
+
filteredSettings.firebaseApiKey = settings.firebaseApiKey;
|
|
26
26
|
if (settings.beginner !== undefined)
|
|
27
27
|
filteredSettings.beginner = settings.beginner;
|
|
28
28
|
if (settings.fairPlay !== undefined)
|
|
@@ -88,7 +88,7 @@ const applyPersistedSettings = (state) => {
|
|
|
88
88
|
account: state.account || persisted.account || state.userId, // Use userId as fallback
|
|
89
89
|
userId: state.userId || persisted.userId || state.userId,
|
|
90
90
|
userNick: state.userNick || persisted.userNick || state.userNick,
|
|
91
|
-
|
|
91
|
+
firebaseApiKey: state.firebaseApiKey || persisted.firebaseApiKey || state.firebaseApiKey,
|
|
92
92
|
beginner: (_a = persisted.beginner) !== null && _a !== void 0 ? _a : state.beginner,
|
|
93
93
|
fairPlay: (_b = persisted.fairPlay) !== null && _b !== void 0 ? _b : state.fairPlay,
|
|
94
94
|
ready: (_c = persisted.ready) !== null && _c !== void 0 ? _c : state.ready,
|
|
@@ -98,8 +98,8 @@ const applyPersistedSettings = (state) => {
|
|
|
98
98
|
|
|
99
99
|
const DEFAULT_STATE = {
|
|
100
100
|
projectId: "netskrafl",
|
|
101
|
-
|
|
102
|
-
|
|
101
|
+
firebaseApiKey: "",
|
|
102
|
+
databaseUrl: "",
|
|
103
103
|
firebaseSenderId: "",
|
|
104
104
|
firebaseAppId: "",
|
|
105
105
|
measurementId: "",
|
|
@@ -27213,12 +27213,12 @@ let database;
|
|
|
27213
27213
|
let analytics;
|
|
27214
27214
|
function initFirebase(state) {
|
|
27215
27215
|
try {
|
|
27216
|
-
const { projectId,
|
|
27216
|
+
const { projectId, firebaseApiKey, databaseUrl, firebaseSenderId, firebaseAppId, measurementId } = state;
|
|
27217
27217
|
const firebaseOptions = {
|
|
27218
27218
|
projectId,
|
|
27219
|
-
apiKey:
|
|
27219
|
+
apiKey: firebaseApiKey,
|
|
27220
27220
|
authDomain: `${projectId}.firebaseapp.com`,
|
|
27221
|
-
databaseURL,
|
|
27221
|
+
databaseURL: databaseUrl,
|
|
27222
27222
|
storageBucket: `${projectId}.firebasestorage.app`,
|
|
27223
27223
|
messagingSenderId: firebaseSenderId,
|
|
27224
27224
|
appId: firebaseAppId,
|
|
@@ -27649,7 +27649,7 @@ const ensureAuthenticated = async (state) => {
|
|
|
27649
27649
|
// Update the user's nickname
|
|
27650
27650
|
state.userNick = result.nickname || state.userNick;
|
|
27651
27651
|
// Use the server's Firebase API key, if provided
|
|
27652
|
-
state.
|
|
27652
|
+
state.firebaseApiKey = result.firebase_api_key || state.firebaseApiKey;
|
|
27653
27653
|
// Load state flags and preferences
|
|
27654
27654
|
state.beginner = (_b = (_a = result.prefs) === null || _a === void 0 ? void 0 : _a.beginner) !== null && _b !== void 0 ? _b : true;
|
|
27655
27655
|
state.fairPlay = (_d = (_c = result.prefs) === null || _c === void 0 ? void 0 : _c.fairplay) !== null && _d !== void 0 ? _d : false;
|
|
@@ -27660,7 +27660,7 @@ const ensureAuthenticated = async (state) => {
|
|
|
27660
27660
|
userEmail: state.userEmail, // CRITICAL: Include email to validate ownership
|
|
27661
27661
|
userId: state.userId,
|
|
27662
27662
|
userNick: state.userNick,
|
|
27663
|
-
|
|
27663
|
+
firebaseApiKey: state.firebaseApiKey,
|
|
27664
27664
|
beginner: state.beginner,
|
|
27665
27665
|
fairPlay: state.fairPlay,
|
|
27666
27666
|
ready: state.ready,
|
|
@@ -30890,14 +30890,16 @@ const Board = (initialVnode) => {
|
|
|
30890
30890
|
}
|
|
30891
30891
|
return {
|
|
30892
30892
|
view: (vnode) => {
|
|
30893
|
-
const scale = view.boardScale || 1.0;
|
|
30893
|
+
// const scale = view.boardScale || 1.0;
|
|
30894
30894
|
let attrs = {};
|
|
30895
30895
|
// Add handlers for pinch zoom functionality
|
|
30896
30896
|
// Note: resist the temptation to pass zoomIn/zoomOut directly,
|
|
30897
30897
|
// as that would not bind the 'this' pointer correctly
|
|
30898
30898
|
addPinchZoom(attrs, () => view.zoomIn(), () => view.zoomOut());
|
|
30899
|
+
/*
|
|
30899
30900
|
if (scale !== 1.0)
|
|
30900
|
-
|
|
30901
|
+
attrs.style = `transform: scale(${scale})`;
|
|
30902
|
+
*/
|
|
30901
30903
|
return m(".board", { id: "board-parent" }, m("table.board", attrs, m("tbody", allrows())));
|
|
30902
30904
|
}
|
|
30903
30905
|
};
|
|
@@ -31666,12 +31668,12 @@ const RightColumn = (initialVnode) => {
|
|
|
31666
31668
|
*/
|
|
31667
31669
|
const Beginner = (initialVnode) => {
|
|
31668
31670
|
// Show the board color guide
|
|
31669
|
-
const { view } = initialVnode.attrs;
|
|
31671
|
+
const { view, showClose } = initialVnode.attrs;
|
|
31670
31672
|
const { model, actions } = view;
|
|
31671
31673
|
const state = model.state;
|
|
31672
31674
|
return {
|
|
31673
31675
|
view: () => m(".board-help", { title: ts("Hvernig reitirnir margfalda stigin") }, [
|
|
31674
|
-
m(".board-help-close", {
|
|
31676
|
+
showClose ? m(".board-help-close", {
|
|
31675
31677
|
title: ts("Loka þessari hjálp"),
|
|
31676
31678
|
onclick: (ev) => {
|
|
31677
31679
|
// Close the guide and set a preference not to see it again
|
|
@@ -31681,7 +31683,7 @@ const Beginner = (initialVnode) => {
|
|
|
31681
31683
|
}
|
|
31682
31684
|
ev.preventDefault();
|
|
31683
31685
|
}
|
|
31684
|
-
}, glyph("remove")),
|
|
31686
|
+
}, glyph("remove")) : null,
|
|
31685
31687
|
m(".board-colors", [
|
|
31686
31688
|
m(".board-color[id='triple-word']", ["3 x", m("br"), t("orð")]),
|
|
31687
31689
|
m(".board-color[id='double-word']", ["2 x", m("br"), t("orð")]),
|
|
@@ -31737,7 +31739,7 @@ const GameView = {
|
|
|
31737
31739
|
// These elements appear after the game container, since we want
|
|
31738
31740
|
// them to be above it in the z-order
|
|
31739
31741
|
m(LeftLogo),
|
|
31740
|
-
(state === null || state === void 0 ? void 0 : state.beginner) ? m(Beginner, { view }) : "",
|
|
31742
|
+
(state === null || state === void 0 ? void 0 : state.beginner) ? m(Beginner, { view, showClose: true }) : "",
|
|
31741
31743
|
m(Info),
|
|
31742
31744
|
]);
|
|
31743
31745
|
}
|
|
@@ -32264,6 +32266,8 @@ class View {
|
|
|
32264
32266
|
this.dialogStack = [];
|
|
32265
32267
|
// The current scaling of the board
|
|
32266
32268
|
this.boardScale = 1.0;
|
|
32269
|
+
// Pending zoom animation timeout (for cancellation across rapid zoom operations)
|
|
32270
|
+
this.zoomAnimationTimeout = null;
|
|
32267
32271
|
this.selectedTab = "movelist";
|
|
32268
32272
|
this.actions = actions;
|
|
32269
32273
|
// Initialize media listeners now that we have the view reference
|
|
@@ -32407,13 +32411,17 @@ class View {
|
|
|
32407
32411
|
}
|
|
32408
32412
|
resetScale() {
|
|
32409
32413
|
// Reset the board scale (zoom) to 100% and the scroll origin to (0, 0)
|
|
32414
|
+
// Cancel any pending zoom animation
|
|
32415
|
+
if (this.zoomAnimationTimeout !== null) {
|
|
32416
|
+
clearTimeout(this.zoomAnimationTimeout);
|
|
32417
|
+
this.zoomAnimationTimeout = null;
|
|
32418
|
+
}
|
|
32410
32419
|
this.boardScale = 1.0;
|
|
32411
32420
|
const boardParent = document.getElementById("board-parent");
|
|
32412
32421
|
const board = boardParent === null || boardParent === void 0 ? void 0 : boardParent.children[0];
|
|
32413
32422
|
if (board) {
|
|
32414
32423
|
board.style.transition = 'none';
|
|
32415
|
-
board.style.
|
|
32416
|
-
board.style.transform = `translate(0, 0) scale(1.0)`;
|
|
32424
|
+
board.style.transform = `translate(0px, 0px) scale(1)`;
|
|
32417
32425
|
}
|
|
32418
32426
|
if (boardParent)
|
|
32419
32427
|
boardParent.scrollTo(0, 0);
|
|
@@ -32424,7 +32432,7 @@ class View {
|
|
|
32424
32432
|
// Use either the regular game or the riddle (Gáta Dagsins)
|
|
32425
32433
|
const game = model.game || model.riddle;
|
|
32426
32434
|
// Update the board scale (zoom)
|
|
32427
|
-
|
|
32435
|
+
const scrollIntoView = (sq) => {
|
|
32428
32436
|
// Scroll a square above and to the left of the placed tile into view,
|
|
32429
32437
|
// with a smooth concurrent zoom and pan animation,
|
|
32430
32438
|
// taking clamping into account to ensure that the board always fills
|
|
@@ -32438,9 +32446,7 @@ class View {
|
|
|
32438
32446
|
const row = Math.max(0, vec.row - offset);
|
|
32439
32447
|
const col = Math.max(0, vec.col - offset);
|
|
32440
32448
|
const c = coord(row, col);
|
|
32441
|
-
//
|
|
32442
|
-
board.style.transformOrigin = 'top left';
|
|
32443
|
-
board.style.transform = `translate(0, 0) scale(1.0)`;
|
|
32449
|
+
// board.style.transform = `translate(0, 0) scale(1.0)`;
|
|
32444
32450
|
const el = document.getElementById("sq_" + c);
|
|
32445
32451
|
const elRect = el === null || el === void 0 ? void 0 : el.getBoundingClientRect();
|
|
32446
32452
|
const boardRect = boardParent.getBoundingClientRect();
|
|
@@ -32458,22 +32464,32 @@ class View {
|
|
|
32458
32464
|
// the top left corner of the viewport, or as close as possible
|
|
32459
32465
|
let targetScrollLeft = Math.min(elRect.left - boardRect.left, maxScrollLeft);
|
|
32460
32466
|
let targetScrollTop = Math.min(elRect.top - boardRect.top, maxScrollTop);
|
|
32467
|
+
// Cancel any pending zoom animation
|
|
32468
|
+
if (this.zoomAnimationTimeout !== null) {
|
|
32469
|
+
clearTimeout(this.zoomAnimationTimeout);
|
|
32470
|
+
this.zoomAnimationTimeout = null;
|
|
32471
|
+
}
|
|
32461
32472
|
// Now animate both translate (for pan) and scale (for zoom) concurrently
|
|
32462
|
-
board.style.transition = 'transform 0.3s ease-in-out';
|
|
32463
32473
|
// Note: transforms are applied right to left
|
|
32464
|
-
board.style.transform
|
|
32465
|
-
|
|
32466
|
-
|
|
32467
|
-
board.
|
|
32468
|
-
|
|
32474
|
+
board.style.transition = 'transform 0.3s ease-in-out';
|
|
32475
|
+
const translateValue = `${-targetScrollLeft}px, ${-targetScrollTop}px`;
|
|
32476
|
+
const scaleValue = `${ZOOM_FACTOR}`;
|
|
32477
|
+
board.style.transform = `translate(${translateValue}) scale(${scaleValue})`;
|
|
32478
|
+
// Use setTimeout as primary mechanism (transitionend is unreliable in Firefox/Safari)
|
|
32479
|
+
// 350ms = 300ms animation + 50ms buffer
|
|
32480
|
+
this.zoomAnimationTimeout = setTimeout(() => {
|
|
32481
|
+
this.zoomAnimationTimeout = null;
|
|
32482
|
+
// Reset the transform (remove translate, keep scale)
|
|
32469
32483
|
board.style.transition = 'none';
|
|
32470
|
-
board.style.transform = `translate(
|
|
32471
|
-
//
|
|
32472
|
-
|
|
32473
|
-
|
|
32474
|
-
|
|
32484
|
+
board.style.transform = `translate(0px, 0px) scale(${ZOOM_FACTOR})`;
|
|
32485
|
+
// Wait for the browser to process the style changes before setting scroll position
|
|
32486
|
+
// This is needed for Firefox and Safari to properly update the scroll position
|
|
32487
|
+
requestAnimationFrame(() => {
|
|
32488
|
+
boardParent.scrollTo(targetScrollLeft, targetScrollTop);
|
|
32489
|
+
});
|
|
32490
|
+
}, 350);
|
|
32475
32491
|
}
|
|
32476
|
-
}
|
|
32492
|
+
};
|
|
32477
32493
|
if (!game || ((_a = model.state) === null || _a === void 0 ? void 0 : _a.uiFullscreen) || game.moveInProgress) {
|
|
32478
32494
|
// No game or we're in full screen mode: always 100% scale
|
|
32479
32495
|
// Also, as soon as a move is being processed by the server, we zoom out
|
|
@@ -36610,27 +36626,25 @@ const SunCorona = {
|
|
|
36610
36626
|
const MobileStatus = () => {
|
|
36611
36627
|
return {
|
|
36612
36628
|
view: (vnode) => {
|
|
36613
|
-
const {
|
|
36614
|
-
const {
|
|
36629
|
+
const { view, selectedMoves, bestMove, onMoveClick, onStatsClick } = vnode.attrs;
|
|
36630
|
+
const { riddle } = view.model;
|
|
36631
|
+
if (!riddle)
|
|
36632
|
+
return null;
|
|
36633
|
+
const { bestPossibleScore, globalBestScore, personalBestScore } = riddle;
|
|
36615
36634
|
// Determine if player achieved best possible score
|
|
36616
36635
|
const achieved = bestMove !== undefined;
|
|
36617
36636
|
const celebrate = bestMove && bestMove.word !== "";
|
|
36618
|
-
// Get player's current best score
|
|
36619
|
-
// If the player achieved the best possible score, it's in bestMove (not selectedMoves)
|
|
36620
|
-
const playerBestScore = (bestMove && bestMove.word !== "")
|
|
36621
|
-
? bestMove.score
|
|
36622
|
-
: (selectedMoves.length > 0 ? selectedMoves[0].score : 0);
|
|
36623
36637
|
// Determine current leader score (may be this player or another)
|
|
36624
36638
|
let leaderScore = 0;
|
|
36625
36639
|
let isPlayerLeading = false;
|
|
36626
36640
|
if (globalBestScore && globalBestScore.score > 0) {
|
|
36627
36641
|
leaderScore = globalBestScore.score;
|
|
36628
36642
|
// Check if player is leading
|
|
36629
|
-
isPlayerLeading =
|
|
36643
|
+
isPlayerLeading = personalBestScore >= globalBestScore.score;
|
|
36630
36644
|
}
|
|
36631
36645
|
else {
|
|
36632
|
-
leaderScore =
|
|
36633
|
-
isPlayerLeading =
|
|
36646
|
+
leaderScore = personalBestScore;
|
|
36647
|
+
isPlayerLeading = personalBestScore > 0;
|
|
36634
36648
|
}
|
|
36635
36649
|
return m(".mobile-status-container", [
|
|
36636
36650
|
// Current word score (leftmost) - uses RiddleScore component in mobile mode
|
|
@@ -36642,12 +36656,12 @@ const MobileStatus = () => {
|
|
|
36642
36656
|
}, [
|
|
36643
36657
|
// Player's best score
|
|
36644
36658
|
m(".mobile-status-card-item.player-best", [
|
|
36645
|
-
m(".mobile-status-label", ts("Þín besta
|
|
36646
|
-
m(".mobile-status-score",
|
|
36659
|
+
m(".mobile-status-label", ts("Þín besta")),
|
|
36660
|
+
m(".mobile-status-score", personalBestScore.toString())
|
|
36647
36661
|
]),
|
|
36648
36662
|
// Current leader score
|
|
36649
36663
|
m(".mobile-status-card-item.leader" + (isPlayerLeading ? ".is-player" : ""), [
|
|
36650
|
-
m(".mobile-status-label", isPlayerLeading ? ts("Þú leiðir!") : ts("
|
|
36664
|
+
m(".mobile-status-label", isPlayerLeading ? ts("Þú leiðir!") : ts("Best til þessa")),
|
|
36651
36665
|
m(".mobile-status-score", leaderScore.toString())
|
|
36652
36666
|
]),
|
|
36653
36667
|
// Chevron indicator (overlaid at bottom center)
|
|
@@ -36691,8 +36705,11 @@ const TabBar = {
|
|
|
36691
36705
|
key: tab.id,
|
|
36692
36706
|
onclick: () => onTabChange(tab.id)
|
|
36693
36707
|
}, [
|
|
36694
|
-
|
|
36695
|
-
tab.
|
|
36708
|
+
m("span.tab-icon-wrapper", [
|
|
36709
|
+
tab.iconGlyph ? m("span.tab-icon", glyph(tab.iconGlyph)) :
|
|
36710
|
+
tab.icon ? m("span.tab-icon", tab.icon) : null,
|
|
36711
|
+
tab.badgeGlyph ? m("span.tab-badge", glyph(tab.badgeGlyph)) : null
|
|
36712
|
+
]),
|
|
36696
36713
|
m("span.tab-label", tab.label)
|
|
36697
36714
|
])));
|
|
36698
36715
|
}
|
|
@@ -36781,7 +36798,7 @@ const BestPossibleScore = () => {
|
|
|
36781
36798
|
}
|
|
36782
36799
|
else {
|
|
36783
36800
|
// Someone else achieved it - indicate this
|
|
36784
|
-
topLabel = ts("
|
|
36801
|
+
topLabel = ts("Besta mögulega lögn\n(er þegar fundin)");
|
|
36785
36802
|
}
|
|
36786
36803
|
}
|
|
36787
36804
|
else {
|
|
@@ -36831,7 +36848,7 @@ const PlayerMovesOverlay = () => {
|
|
|
36831
36848
|
const allMoveElements = [];
|
|
36832
36849
|
function scoreDetails(move) {
|
|
36833
36850
|
if (move.isGlobalBestScore) {
|
|
36834
|
-
if (move.word === ""
|
|
36851
|
+
if (move.word === "") {
|
|
36835
36852
|
// Another player holds the top score
|
|
36836
36853
|
return [m(GlobalBestScore, { thisPlayer: false, score: move.score })];
|
|
36837
36854
|
}
|
|
@@ -37061,18 +37078,26 @@ const LeaderboardView = {
|
|
|
37061
37078
|
const RightSideTabs = () => {
|
|
37062
37079
|
// Component-local state for active tab (defaults to performance)
|
|
37063
37080
|
let activeTab = "performance";
|
|
37064
|
-
const tabs = [
|
|
37065
|
-
{ id: "performance", label: ts("Frammistaða"), iconGlyph: "dashboard" },
|
|
37066
|
-
{ id: "stats", label: ts("Tölfræði"), iconGlyph: "stats" },
|
|
37067
|
-
{ id: "leaderboard", label: ts("Stigatafla"), iconGlyph: "tower" }
|
|
37068
|
-
];
|
|
37069
37081
|
return {
|
|
37070
37082
|
view: (vnode) => {
|
|
37071
37083
|
const { view, selectedMoves, bestMove, onMoveClick } = vnode.attrs;
|
|
37072
|
-
const { riddle, state } = view.model;
|
|
37084
|
+
const { riddle, state, leaderboard } = view.model;
|
|
37073
37085
|
if (!riddle) {
|
|
37074
37086
|
return m(".gatadagsins-right-side-tabs", "");
|
|
37075
37087
|
}
|
|
37088
|
+
// Check if current user is on the leaderboard
|
|
37089
|
+
const currentUserId = (state === null || state === void 0 ? void 0 : state.userId) || "";
|
|
37090
|
+
const isOnLeaderboard = leaderboard && leaderboard.some(entry => entry.userId === currentUserId);
|
|
37091
|
+
const tabs = [
|
|
37092
|
+
{ id: "performance", label: ts("Frammistaða"), iconGlyph: "dashboard" },
|
|
37093
|
+
{ id: "stats", label: ts("Tölfræði"), iconGlyph: "stats" },
|
|
37094
|
+
{
|
|
37095
|
+
id: "leaderboard",
|
|
37096
|
+
label: ts("Stigatafla"),
|
|
37097
|
+
iconGlyph: "tower",
|
|
37098
|
+
badgeGlyph: isOnLeaderboard ? "star" : undefined
|
|
37099
|
+
}
|
|
37100
|
+
];
|
|
37076
37101
|
const handleTabChange = (tabId) => {
|
|
37077
37102
|
activeTab = tabId;
|
|
37078
37103
|
};
|
|
@@ -37138,7 +37163,7 @@ const GataDagsinsRightSide = {
|
|
|
37138
37163
|
return m(".gatadagsins-right-side-wrapper", riddle ? [
|
|
37139
37164
|
// Mobile-only status bar (visible on mobile, hidden on desktop)
|
|
37140
37165
|
m(".gatadagsins-mobile-status", m(MobileStatus, {
|
|
37141
|
-
|
|
37166
|
+
view,
|
|
37142
37167
|
selectedMoves,
|
|
37143
37168
|
bestMove,
|
|
37144
37169
|
onMoveClick: handleMoveClick,
|
|
@@ -37258,31 +37283,39 @@ const GataDagsinsHelp = {
|
|
|
37258
37283
|
const StatsModal = () => {
|
|
37259
37284
|
// Component-local state for active tab (defaults to stats)
|
|
37260
37285
|
let activeTab = "stats";
|
|
37261
|
-
const tabs = [
|
|
37262
|
-
{ id: "stats", label: ts("Tölfræði"), iconGlyph: "stats" },
|
|
37263
|
-
{ id: "leaderboard", label: ts("Stigatafla"), iconGlyph: "tower" }
|
|
37264
|
-
];
|
|
37265
37286
|
return {
|
|
37266
37287
|
view: (vnode) => {
|
|
37267
37288
|
const { view, onClose } = vnode.attrs;
|
|
37268
|
-
const { riddle, state } = view.model;
|
|
37289
|
+
const { riddle, state, leaderboard } = view.model;
|
|
37269
37290
|
if (!riddle) {
|
|
37270
37291
|
return null;
|
|
37271
37292
|
}
|
|
37293
|
+
// Check if current user is on the leaderboard
|
|
37294
|
+
const currentUserId = (state === null || state === void 0 ? void 0 : state.userId) || "";
|
|
37295
|
+
const isOnLeaderboard = leaderboard && leaderboard.some(entry => entry.userId === currentUserId);
|
|
37296
|
+
const tabs = [
|
|
37297
|
+
{ id: "stats", label: ts("Tölfræði"), iconGlyph: "stats" },
|
|
37298
|
+
{
|
|
37299
|
+
id: "leaderboard",
|
|
37300
|
+
label: ts("Stigatafla"),
|
|
37301
|
+
iconGlyph: "tower",
|
|
37302
|
+
badgeGlyph: isOnLeaderboard ? "star" : undefined
|
|
37303
|
+
}
|
|
37304
|
+
];
|
|
37272
37305
|
const handleTabChange = (tabId) => {
|
|
37273
37306
|
activeTab = tabId;
|
|
37274
37307
|
};
|
|
37275
37308
|
return [
|
|
37276
37309
|
// Backdrop
|
|
37277
37310
|
m(".modal-backdrop-netskrafl", {
|
|
37278
|
-
onclick: (e) => { e.preventDefault(); },
|
|
37279
|
-
onwheel: (e) => { e.preventDefault(); e.stopPropagation(); },
|
|
37280
|
-
ontouchmove: (e) => { e.preventDefault(); e.stopPropagation(); }
|
|
37311
|
+
onclick: (e) => { e.preventDefault(); return false; },
|
|
37312
|
+
onwheel: (e) => { e.preventDefault(); e.stopPropagation(); return false; },
|
|
37313
|
+
ontouchmove: (e) => { e.preventDefault(); e.stopPropagation(); return false; }
|
|
37281
37314
|
}),
|
|
37282
37315
|
// Modal dialog
|
|
37283
37316
|
m(".modal-dialog.stats-modal", {
|
|
37284
|
-
onwheel: (e) => { e.stopPropagation(); },
|
|
37285
|
-
ontouchmove: (e) => { e.stopPropagation(); }
|
|
37317
|
+
onwheel: (e) => { e.stopPropagation(); return false; },
|
|
37318
|
+
ontouchmove: (e) => { e.stopPropagation(); return false; }
|
|
37286
37319
|
}, [
|
|
37287
37320
|
m(".modal-content", [
|
|
37288
37321
|
// Close button in top right
|
|
@@ -37415,9 +37448,9 @@ const GataDagsins$1 = () => {
|
|
|
37415
37448
|
id: "gatadagsins-background",
|
|
37416
37449
|
}, [
|
|
37417
37450
|
// The main content area
|
|
37418
|
-
|
|
37451
|
+
m(".gatadagsins-container", [
|
|
37419
37452
|
// Main display area with flex layout
|
|
37420
|
-
m(".gatadagsins-main", [
|
|
37453
|
+
riddle ? m(".gatadagsins-main", [
|
|
37421
37454
|
// Board and rack component (left side)
|
|
37422
37455
|
m(GataDagsinsBoardAndRack, { view }),
|
|
37423
37456
|
// Right-side component with scores and comparisons
|
|
@@ -37426,17 +37459,17 @@ const GataDagsins$1 = () => {
|
|
|
37426
37459
|
riddle.askingForBlank
|
|
37427
37460
|
? m(BlankDialog, { game: riddle })
|
|
37428
37461
|
: "",
|
|
37429
|
-
])
|
|
37430
|
-
|
|
37431
|
-
|
|
37432
|
-
|
|
37433
|
-
|
|
37434
|
-
|
|
37435
|
-
|
|
37436
|
-
|
|
37437
|
-
|
|
37438
|
-
|
|
37439
|
-
|
|
37462
|
+
]) : "",
|
|
37463
|
+
// The left margin elements: back button and info/help button
|
|
37464
|
+
// These elements appear after the main container for proper z-order
|
|
37465
|
+
// m(LeftLogo), // Currently no need for the logo for Gáta Dagsins
|
|
37466
|
+
// Show the Beginner component if the user is a beginner
|
|
37467
|
+
((_a = model.state) === null || _a === void 0 ? void 0 : _a.beginner) ? m(Beginner, { view, showClose: false }) : "",
|
|
37468
|
+
// Custom Info button for GataDagsins that shows help dialog
|
|
37469
|
+
m(".info", { title: ts("Upplýsingar og hjálp") }, m("a.iconlink", { href: "#", onclick: (e) => { e.preventDefault(); toggleHelp(); } }, glyph("info-sign"))),
|
|
37470
|
+
// Help dialog and backdrop
|
|
37471
|
+
showHelp ? m(GataDagsinsHelp, { onClose: toggleHelp }) : "",
|
|
37472
|
+
]),
|
|
37440
37473
|
// Stats modal and backdrop (mobile only)
|
|
37441
37474
|
showStatsModal ? m(StatsModal, { view, onClose: toggleStatsModal }) : "",
|
|
37442
37475
|
]);
|