@mideind/netskrafl-react 2.1.0 → 2.2.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/README.md CHANGED
@@ -1,63 +1,138 @@
1
1
 
2
2
  # netskrafl-react
3
3
 
4
- ## A React front end for Netskrafl
4
+ A React front end for Netskrafl, packaged as a React component library.
5
5
 
6
- This is a web front end for the Netskrafl game server, packaged as a React component.
6
+ ## Components
7
7
 
8
- The component is written in TypeScript. It assumes that the containing application
9
- (a) performs user authentication and (b) determines whether users have full access to the
10
- Netskrafl functionality (typically by being paying subscribers) or limited/free access.
11
- Information about (a) and (b) is passed to the component via its props.
8
+ This library provides two main components:
12
9
 
13
- The code for the Netskrafl backend server can be found on GitHub
14
- at [https://github.com/mideind/Netskrafl](https://github.com/mideind/Netskrafl).
15
- The backend is written in Python, using the Flask web framework.
16
- The `netskrafl-react` component talks to the Netskrafl server via a
17
- JSON API over HTTPS, and uses Firebase for real-time updates.
10
+ - **Netskrafl** - The full multiplayer crossword game
11
+ - **GataDagsins** - A daily crossword riddle ("Riddle of the Day")
18
12
 
19
- The source code for `netskrafl-react` is a bit unusual in that it was originally
20
- written for the [Mithril](https://mithril.js.org) UI framework. The Mithril code
21
- has been wrapped in a React component. Modifying the component thus requires
22
- some familiarity with Mithril, as well as React. However, Mithril is fairly
23
- straightforward and arguably simpler than React, so this should not be a major
24
- obstacle.
13
+ Both components are responsive and mobile-friendly.
25
14
 
26
15
  ## Installation
27
16
 
28
- To install the Netskrafl React component via `npm`, run the following command:
17
+ ```bash
18
+ npm install @mideind/netskrafl-react
19
+ ```
29
20
 
21
+ Requires React 18+. This package is distributed as ESM only.
22
+
23
+ ## Usage
24
+
25
+ Import the component and its styles:
26
+
27
+ ```tsx
28
+ import { Netskrafl } from '@mideind/netskrafl-react';
29
+ import '@mideind/netskrafl-react/style.css';
30
+
31
+ function App() {
32
+ return (
33
+ <Netskrafl
34
+ state={{
35
+ userEmail: 'user@example.com',
36
+ userFullName: 'Example User',
37
+ userId: 'unique-user-id',
38
+ firebaseToken: 'firebase-auth-token',
39
+ subscriber: true,
40
+ locale: 'is',
41
+ urlPrefix: 'https://netskrafl.is',
42
+ }}
43
+ />
44
+ );
45
+ }
30
46
  ```
31
- npm install @mideind/netskrafl-react
47
+
48
+ For Gáta Dagsins:
49
+
50
+ ```tsx
51
+ import { GataDagsins } from '@mideind/netskrafl-react';
52
+ import '@mideind/netskrafl-react/style.css';
53
+
54
+ function App() {
55
+ return (
56
+ <GataDagsins
57
+ state={{
58
+ userEmail: 'user@example.com',
59
+ userFullName: 'Example User',
60
+ userId: 'unique-user-id',
61
+ firebaseToken: 'firebase-auth-token',
62
+ subscriber: true,
63
+ locale: 'is',
64
+ urlPrefix: 'https://netskrafl.is',
65
+ }}
66
+ />
67
+ );
68
+ }
32
69
  ```
33
70
 
34
- ## Development
71
+ ### Props
35
72
 
36
- During development, you can run the rollup bundler in watch mode by running:
73
+ | Prop | Type | Description |
74
+ |------|------|-------------|
75
+ | `userEmail` | string | User's email address |
76
+ | `userFullName` | string | User's display name |
77
+ | `userId` | string | Unique user identifier |
78
+ | `firebaseToken` | string | Firebase authentication token for real-time updates |
79
+ | `subscriber` | boolean | Whether user has full access (subscriber) or limited/free access |
80
+ | `locale` | string | UI language (`'is'` for Icelandic, `'en'` for English) |
81
+ | `urlPrefix` | string | Base URL of the Netskrafl backend server |
37
82
 
38
- ```bash
39
- npm run watch
40
- ```
83
+ ## Features
41
84
 
42
- You can also use Storybook as a testing container by running:
85
+ - Drag-and-drop tile placement
86
+ - Click-to-select tile placement
87
+ - Keyboard tile placement (type letters to place tiles)
88
+ - Real-time game updates via Firebase
89
+ - Mobile-responsive design with touch support
90
+ - Pinch-to-zoom on mobile devices
43
91
 
44
- ```bash
45
- npm run storybook
46
- ```
92
+ ## Architecture
93
+
94
+ The components are written in TypeScript. They assume that the containing
95
+ application handles user authentication and subscription status.
47
96
 
48
- To build the component for production, run:
97
+ The code uses a hybrid React-Mithril architecture: the components are React
98
+ wrappers around [Mithril](https://mithril.js.org) UI code. Modifying the
99
+ components requires familiarity with both frameworks, though Mithril is
100
+ straightforward and arguably simpler than React.
101
+
102
+ The components communicate with the Netskrafl backend server via a JSON API
103
+ over HTTPS, and use Firebase for real-time updates.
104
+
105
+ ## Backend
106
+
107
+ The Netskrafl backend server code is available at
108
+ [github.com/mideind/Netskrafl](https://github.com/mideind/Netskrafl).
109
+ The backend is written in Python using the Flask web framework.
110
+
111
+ ## Development
49
112
 
50
113
  ```bash
114
+ # Install dependencies
115
+ npm install
116
+
117
+ # Run rollup in watch mode
118
+ npm run watch
119
+
120
+ # Run Storybook for development/testing
121
+ npm run storybook
122
+
123
+ # Build for production
51
124
  npm run rollup
52
- ```
53
125
 
54
- See the `package.json` file for specifics.
126
+ # Type-check
127
+ npx tsc --noEmit
128
+
129
+ # Lint
130
+ npx @biomejs/biome check src/
131
+ ```
55
132
 
56
- ## License and copyright
133
+ ## License
57
134
 
58
- Copyright © 2025 Miðeind ehf. The original author of `netskrafl-react`
59
- is *Vilhjálmur Þorsteinsson*.
135
+ Copyright © 2025 Miðeind ehf. Original author: Vilhjálmur Þorsteinsson.
60
136
 
61
- This code is released under the
62
- [CC-BY-NC 4.0 license](https://creativecommons.org/licenses/by-nc/4.0/).
137
+ Released under the [CC-BY-NC 4.0 license](https://creativecommons.org/licenses/by-nc/4.0/).
63
138
  See the LICENSE file for details.
@@ -319,9 +319,6 @@
319
319
 
320
320
  */
321
321
 
322
- /*
323
- */
324
-
325
322
  div.netskrafl-container {
326
323
  --logo-primary: #c94314;
327
324
  --logo-primary-light: hsl(from var(--logo-primary) h s 75%);
@@ -507,7 +504,6 @@ div.netskrafl-container * {
507
504
 
508
505
  .netskrafl-container .ui-tabs {
509
506
  position: relative;
510
- /* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */
511
507
  padding: 3px;
512
508
  border-width: 0;
513
509
  }
@@ -711,11 +707,11 @@ div.netskrafl-container * {
711
707
  text-transform: uppercase;
712
708
  }
713
709
 
714
- @media all and (max-width: 360px) {
715
- /* Hack to squeeze the content on 360 pixel wide phones */
710
+ @media all and (max-width: 374px) and (orientation: portrait) {
711
+ /* Scale down for phones narrower than the 375px design width */
716
712
  div.netskrafl-container {
717
713
  transform: scale(0.96, 1);
718
- /* 0.96 = 360/375 */
714
+ /* 0.96 = 360/375, scales 375px design to fit 360px viewport */
719
715
  transform-origin: left top;
720
716
  }
721
717
  }
@@ -6083,13 +6079,14 @@ div.netskrafl-container input[type="checkbox"] {
6083
6079
 
6084
6080
  /* ================= MOBILE LANDSCAPE UI ====================== */
6085
6081
 
6086
- @media all and (min-width: 667px) {
6087
- /* Mobile screen, landscape mode */
6082
+ @media all and (min-width: 667px) and (orientation: landscape) {
6083
+ /* Mobile screen, landscape mode (width >= 667px AND landscape orientation) */
6088
6084
  div.netskrafl-container {
6089
6085
  /* Reference width */
6090
6086
  width: 667px;
6091
6087
  /* Reference height */
6092
- height: 375px;
6088
+ height: calc(100dvh - var(--header-size, 3rem));
6089
+ max-height: 686px;
6093
6090
  overflow-y: hidden;
6094
6091
  }
6095
6092
  .netskrafl-container div.tabbed-page {
@@ -6311,14 +6308,15 @@ div.netskrafl-container input[type="checkbox"] {
6311
6308
  }
6312
6309
  }
6313
6310
 
6314
- @media all and (min-width: 667px) and (max-height: 360px) {
6311
+ @media all and (min-width: 667px) and (orientation: landscape) and (max-height: 360px) {
6312
+ /* Very short landscape screens - scale board down slightly */
6315
6313
  .netskrafl-container div.board {
6316
6314
  transform: scale(0.96);
6317
6315
  transform-origin: top left;
6318
6316
  }
6319
6317
  }
6320
6318
 
6321
- /* fullscreen.css */
6319
+ /* Large screen (iPad and larger) */
6322
6320
 
6323
6321
  @media all and (min-width: 1024px) {
6324
6322
  .netskrafl-container .ui-helper-reset {
@@ -6370,19 +6368,16 @@ div.netskrafl-container input[type="checkbox"] {
6370
6368
  padding-bottom: 1px;
6371
6369
  background-color: var(--tab-background);
6372
6370
  }
6373
- /* Large screen (iPad and larger) */
6374
6371
  div.netskrafl-container {
6375
6372
  width: 1024px;
6376
- height: 768px;
6377
- /* Was 748 */
6373
+ height: 686px;
6378
6374
  overflow-y: hidden;
6379
6375
  }
6380
6376
  .netskrafl-container div.game-container {
6381
- height: 768px;
6377
+ height: 100%;
6382
6378
  }
6383
6379
  .netskrafl-container div.heading {
6384
6380
  height: auto;
6385
- /* background-color: @heading-background; */
6386
6381
  }
6387
6382
  .netskrafl-container div.logo {
6388
6383
  display: block;
@@ -6410,7 +6405,7 @@ div.netskrafl-container input[type="checkbox"] {
6410
6405
  .netskrafl-container div.info {
6411
6406
  display: block;
6412
6407
  position: absolute;
6413
- bottom: 152px;
6408
+ bottom: 52px;
6414
6409
  top: auto;
6415
6410
  left: 28px;
6416
6411
  width: 50px;
@@ -6482,7 +6477,7 @@ div.netskrafl-container input[type="checkbox"] {
6482
6477
  .netskrafl-container div.board-area {
6483
6478
  margin: 0;
6484
6479
  /* width should not be set here */
6485
- height: 750px;
6480
+ height: 746px;
6486
6481
  }
6487
6482
  .netskrafl-container div.board {
6488
6483
  position: absolute;
@@ -7607,7 +7602,6 @@ div.netskrafl-container input[type="checkbox"] {
7607
7602
  width: 428px;
7608
7603
  height: 292px;
7609
7604
  padding-top: 12px;
7610
- /* Override */
7611
7605
  }
7612
7606
  .netskrafl-container span.left-to-move,
7613
7607
  .netskrafl-container span.right-to-move {
@@ -7993,6 +7987,20 @@ div.gatadagsins-board-area.celebrate div.netskrafl-tile.netskrafl-racktile {
7993
7987
  color: #333;
7994
7988
  }
7995
7989
 
7990
+ .netskrafl-container .date-navigator .solver-count-pill {
7991
+ background-color: var(--malfridur-green);
7992
+ color: white;
7993
+ font-family: var(--number-font);
7994
+ font-size: 11px;
7995
+ font-weight: bold;
7996
+ padding: 2px 8px;
7997
+ border-radius: 10px;
7998
+ margin-left: 10px;
7999
+ min-width: 20px;
8000
+ text-align: center;
8001
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
8002
+ }
8003
+
7996
8004
  .netskrafl-container .mobile-date-nav-container {
7997
8005
  display: flex;
7998
8006
  justify-content: center;
@@ -8004,10 +8012,15 @@ div.gatadagsins-board-area.celebrate div.netskrafl-tile.netskrafl-racktile {
8004
8012
  padding: 0;
8005
8013
  width: 100%;
8006
8014
  justify-content: center;
8007
- gap: 20px;
8008
8015
  margin-bottom: 0;
8009
8016
  }
8010
8017
 
8018
+ /* Add spacing between date navigator children (replacing gap) */
8019
+
8020
+ .netskrafl-container .mobile-date-nav-container .date-navigator > * + * {
8021
+ margin-left: 16px;
8022
+ }
8023
+
8011
8024
  /* Mobile status bar - visible on mobile only */
8012
8025
 
8013
8026
  .netskrafl-container div.gatadagsins-mobile-status {
@@ -9227,7 +9240,17 @@ div.gatadagsins-board-area.celebrate div.netskrafl-tile.netskrafl-racktile {
9227
9240
  }
9228
9241
 
9229
9242
  .netskrafl-container div.gatadagsins-container {
9230
- height: 680px;
9243
+ /* Dynamic viewport height minus header, adapts to browser chrome */
9244
+ height: calc(100dvh - var(--header-size, 3rem));
9245
+ max-height: 686px; /* Cap on large screens */
9246
+ }
9247
+
9248
+ .netskrafl-container div.gatadagsins-board-rack-wrapper {
9249
+ justify-content: flex-start; /* Align to the top of the screen */
9250
+ }
9251
+
9252
+ .netskrafl-container div.gatadagsins-board-area {
9253
+ flex: 0 0 auto; /* Don't expand - just fit the board */
9231
9254
  }
9232
9255
 
9233
9256
  .netskrafl-container div.gatadagsins-container div.board {
@@ -9256,7 +9279,7 @@ div.gatadagsins-board-area.celebrate div.netskrafl-tile.netskrafl-racktile {
9256
9279
  }
9257
9280
 
9258
9281
  .netskrafl-container .desktop-date-nav-container {
9259
- margin-bottom: 10px;
9282
+ margin-bottom: 6px;
9260
9283
  }
9261
9284
 
9262
9285
  /* Desktop tab styling adjustments */
@@ -9319,13 +9342,14 @@ div.gatadagsins-board-area.celebrate div.netskrafl-tile.netskrafl-racktile {
9319
9342
  flex-direction: column;
9320
9343
  order: 0;
9321
9344
  margin-top: 0;
9345
+ margin-right: 12px;
9322
9346
  padding-top: 8px;
9323
9347
  padding-bottom: 24px;
9324
9348
  overflow: hidden;
9325
9349
  }
9326
9350
 
9327
9351
  .netskrafl-container div.gatadagsins-rack-area {
9328
- flex: 0 0 120px;
9352
+ flex: 0 0 104px;
9329
9353
  /* Push left to compensate for row ids
9330
9354
  on the left side of the board */
9331
9355
  padding-left: 36px;
@@ -9377,7 +9401,7 @@ div.gatadagsins-board-area.celebrate div.netskrafl-tile.netskrafl-racktile {
9377
9401
  margin-right: 5px;
9378
9402
  }
9379
9403
 
9380
- .netskrafl-container div.gatadagsins-container div.info {
9381
- bottom: 42px;
9404
+ .netskrafl-container div.info {
9405
+ bottom: 72px;
9382
9406
  }
9383
9407
  }